diff --git a/.gitignore b/.gitignore index 1716b8a02..a078dde60 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ target/ Thumbs.db build.sh log/ +temp/ \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5fea0c051..5db282d6a 100644 --- a/pom.xml +++ b/pom.xml @@ -445,26 +445,26 @@ logback-classic ${logback.version} - - org.apache.httpcomponents - httpcore - 4.3.2 - - - org.apache.httpcomponents - httpclient - 4.3.5 - - - commons-logging - commons-logging - - - commons-codec - commons-codec - - - + + org.apache.httpcomponents + httpcore + 4.3.2 + + + org.apache.httpcomponents + httpclient + 4.3.5 + + + commons-logging + commons-logging + + + commons-codec + commons-codec + + + org.antlr antlr @@ -527,6 +527,11 @@ + + org.red5 + red5-server-common + ${project.version} + org.red5 red5-io @@ -612,50 +617,50 @@ compile - bcprov-jdk15on - org.bouncycastle + bcprov-jdk15on + org.bouncycastle ${bc.version} - + - mina-core - org.apache.mina + mina-core + org.apache.mina ${mina.version} - + - mina-integration-jmx - org.apache.mina + mina-integration-jmx + org.apache.mina ${mina.version} - + - mina-integration-beans - org.apache.mina + mina-integration-beans + org.apache.mina ${mina.version} - + - commons-beanutils - commons-beanutils + commons-beanutils + commons-beanutils 1.9.2 - + - commons-codec - commons-codec + commons-codec + commons-codec 1.9 - + - commons-collections - commons-collections + commons-collections + commons-collections 3.2.1 - + - commons-lang3 - org.apache.commons + commons-lang3 + org.apache.commons 3.3.2 - - - org.quartz-scheduler - quartz - ${quartz.version} - + + + org.quartz-scheduler + quartz + ${quartz.version} + org.springframework spring-aop diff --git a/src/main/java/org/red5/logging/Red5LoggerFactory.java b/src/main/java/org/red5/logging/Red5LoggerFactory.java deleted file mode 100644 index f5ab958e0..000000000 --- a/src/main/java/org/red5/logging/Red5LoggerFactory.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.logging; - -import java.lang.reflect.Method; - -import org.red5.server.adapter.StatefulScopeWrappingAdapter; -import org.red5.server.api.scope.IScope; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.impl.StaticLoggerBinder; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.selector.ContextSelector; - -/** - * LoggerFactory to simplify requests for Logger instances within - * Red5 applications. This class is expected to be run only once per - * logger request and is optimized as such. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class Red5LoggerFactory { - - private static boolean useLogback; - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Logger getLogger(Class clazz) { - if (useLogback) { - //determine the red5 app name or servlet context name - String contextName = null; - //if the incoming class extends StatefulScopeWrappingAdapter we lookup the context - //by scope name - boolean scopeAware = StatefulScopeWrappingAdapter.class.isAssignableFrom(clazz); - //System.out.printf("Wrapper - %s\n", StatefulScopeWrappingAdapter.class.isAssignableFrom(clazz)); - if (scopeAware) { - try { - Class wrapper = null; - if ((wrapper = clazz.asSubclass(StatefulScopeWrappingAdapter.class)) != null) { - Method getScope = wrapper.getMethod("getScope", new Class[0]); - //NPE will occur here if the scope is not yet set on the application adapter - IScope scope = (IScope) getScope.invoke(null, new Object[0]); - contextName = scope.getName(); - } - } catch (Exception cce) { - //cclog.warn("Exception {}", e); - } - } else { - //route the Launcher entries to the correct context - String[] parts = Thread.currentThread().getName().split("Launcher:/"); - if (parts.length > 1) { - contextName = parts[1]; - } - - } - - /* TODO: For a future day, the context or application will be determined - //get a reference to our caller - Class caller = Reflection.getCallerClass(2); - //System.err.printf("Caller class: %s classloader: %s\n", caller, caller.getClassLoader()); - - try { - //check to see if we've been called by a servlet - Class sub = caller.asSubclass(Servlet.class); - //System.err.println("Caller is a Servlet"); - - //Method[] methods = caller.getMethods(); - //for (Method meth : methods) { - // System.err.printf("Method: %s\n", meth.getName()); - //} - - Method getContext = caller.getMethod("getServletContext", new Class[0]); - //System.err.printf("got context method - %s\n", getContext); - ServletContext context = (ServletContext) getContext.invoke(caller, null); - System.err.printf("invoked context\n"); - - contextName = context.getServletContextName(); - //System.err.printf("Servlet context name: %s\n", contextName); - - Method getContextName = context.getClass().getMethod("getServletContextName", new Class[0]); - System.err.printf("got context name\n"); - Object ctxName = getContextName.invoke(null, new Object[0]); - - System.err.printf("Servlet context result: %s\n", ctxName); - if (ctxName != null && ctxName instanceof String) { - contextName = ctxName.toString(); - } - } catch (Exception ex) { - //ex.printStackTrace(); - } - */ - return getLogger(clazz, contextName); - } else { - return LoggerFactory.getLogger(clazz); - } - } - - @SuppressWarnings({ "rawtypes" }) - public static Logger getLogger(Class clazz, String contextName) { - if (useLogback) { - Logger logger = null; - try { - //check for logback - Class cs = Class.forName("ch.qos.logback.classic.selector.ContextSelector"); - //trigger an exception if the class doesn't actually exist - cs.getDeclaredMethods(); - // get the class for static binding - cs = Class.forName("org.slf4j.impl.StaticLoggerBinder"); - // get its declared methods - Method[] methods = cs.getDeclaredMethods(); - for (Method method : methods) { - //ensure method exists - if (method.getName().equals("getContextSelector")) { - //System.out.println("Logger context selector method found"); - //get the context selector - StaticLoggerBinder binder = StaticLoggerBinder.getSingleton(); - Method m1 = binder.getClass().getMethod("getContextSelector", (Class[]) null); - ContextSelector selector = (ContextSelector) m1.invoke(binder, (Object[]) null); - //get the context for the given context name or default if null - LoggerContext ctx = null; - if (contextName != null && contextName.length() > 0) { - ctx = selector.getLoggerContext(contextName); - } - // and if we get here, fall back to the default context - if (ctx == null) { - ctx = selector.getLoggerContext(); - } - //debug - //StatusPrinter.print(ctx); - //get the logger from the context or default context - logger = ((ctx != null) ? ctx.getLogger(clazz) : selector.getDefaultLoggerContext().getLogger(clazz)); - break; - } - } - } catch (Exception e) { - //no logback, use whatever logger is in-place - System.err.printf("Exception %s", e.getMessage()); - } - if (logger == null) { - //no logback, use whatever logger is in-place - logger = LoggerFactory.getLogger(clazz); - } - return logger; - } else { - return LoggerFactory.getLogger(clazz); - } - } - - @SuppressWarnings({ "rawtypes" }) - public static Logger getLogger(String name, String contextName) { - Logger logger = null; - try { - //check for logback - Class cs = Class.forName("ch.qos.logback.classic.selector.ContextSelector"); - //trigger an exception if the class doesn't actually exist - cs.getDeclaredMethods(); - // get the class for static binding - cs = Class.forName("org.slf4j.impl.StaticLoggerBinder"); - // get its declared methods - Method[] methods = cs.getDeclaredMethods(); - for (Method method : methods) { - //ensure method exists - if (method.getName().equals("getContextSelector")) { - //System.out.println("Logger context selector method found"); - //get the context selector - StaticLoggerBinder binder = StaticLoggerBinder.getSingleton(); - Method m1 = binder.getClass().getMethod("getContextSelector", (Class[]) null); - ContextSelector selector = (ContextSelector) m1.invoke(binder, (Object[]) null); - //get the context for the given context name or default if null - LoggerContext ctx = null; - if (contextName != null && contextName.length() > 0) { - ctx = selector.getLoggerContext(contextName); - } - // and if we get here, fall back to the default context - if (ctx == null) { - ctx = selector.getLoggerContext(); - } - //debug - //StatusPrinter.print(ctx); - //get the logger from the context or default context - logger = ((ctx != null) ? ctx.getLogger(name) : selector.getDefaultLoggerContext().getLogger(name)); - break; - } - } - } catch (Exception e) { - //no logback, use whatever logger is in-place - System.err.printf("Exception %s", e.getMessage()); - } - if (logger == null) { - //no logback, use whatever logger is in-place - logger = LoggerFactory.getLogger(name); - } - return logger; - } - - public static ContextSelector getContextSelector() { - ContextSelector selector = null; - StaticLoggerBinder binder = StaticLoggerBinder.getSingleton(); - try { - Method m1 = binder.getClass().getMethod("getContextSelector", (Class[]) null); - selector = (ContextSelector) m1.invoke(binder, (Object[]) null); - } catch (Exception e) { - System.err.printf("Exception %s", e.getMessage()); - } - return selector; - } - - public static void setUseLogback(boolean useLogback) { - Red5LoggerFactory.useLogback = useLogback; - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/AttributeStore.java b/src/main/java/org/red5/server/AttributeStore.java deleted file mode 100644 index fcc0c1c2e..000000000 --- a/src/main/java/org/red5/server/AttributeStore.java +++ /dev/null @@ -1,427 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import javax.management.openmbean.CompositeData; - -import org.red5.server.api.IAttributeStore; -import org.red5.server.api.ICastingAttributeStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AttributeStore implements ICastingAttributeStore { - - protected static Logger log = LoggerFactory.getLogger(AttributeStore.class); - - /** - * Map for attributes - */ - protected ConcurrentMap attributes = new ConcurrentAttributesMap(1); - - /** - * Creates empty attribute store. Object is not associated with a persistence storage. - */ - public AttributeStore() { - } - - /** - * Creates attribute store with initial values. Object is not associated with a persistence storage. - * @param values map - */ - public AttributeStore(Map values) { - setAttributes(values); - } - - /** - * Creates attribute store with initial values. Object is not associated with a persistence storage. - * @param values map - */ - public AttributeStore(IAttributeStore values) { - setAttributes(values); - } - - /** - * Filter null keys and values from given map. - * - * @param values the map to filter - * @return filtered map - */ - protected Map filterNull(Map values) { - Map result = new HashMap(); - for (Map.Entry entry : values.entrySet()) { - String key = entry.getKey(); - if (key == null) { - continue; - } - Object value = entry.getValue(); - if (value == null) { - continue; - } - result.put(key, value); - } - return result; - } - - /** - * Get the attribute names. The resulting set will be read-only. - * - * @return set containing all attribute names - */ - public Set getAttributeNames() { - return Collections.unmodifiableSet(attributes.keySet()); - } - - /** - * Get the attributes. The resulting map will be read-only. - * - * @return map containing all attributes - */ - public Map getAttributes() { - return Collections.unmodifiableMap(attributes); - } - - /** - * Return the value for a given attribute. - * - * @param name the name of the attribute to get - * @return the attribute value or null if the attribute doesn't exist - */ - public Object getAttribute(String name) { - if (name == null) { - return null; - } - return attributes.get(name); - } - - /** - * Return the value for a given attribute and set it if it doesn't exist. - * - * @param name the name of the attribute to get - * @param defaultValue the value of the attribute to set if the attribute doesn't - * exist - * @return the attribute value - */ - public Object getAttribute(String name, Object defaultValue) { - if (name == null) { - return null; - } - if (defaultValue == null) { - throw new NullPointerException("the default value may not be null"); - } - Object result = attributes.putIfAbsent(name, defaultValue); - // if no previous value result will be null - if (result == null) { - // use the default value - result = defaultValue; - } - return result; - } - - /** - * Check the object has an attribute. - * - * @param name the name of the attribute to check - * @return true if the attribute exists otherwise false - */ - public boolean hasAttribute(String name) { - if (name == null) { - return false; - } - return attributes.containsKey(name); - } - - /** - * Set an attribute on this object. - * - * @param name the name of the attribute to change - * @param value the new value of the attribute - * @return true if the attribute value was added or changed, otherwise false - */ - public boolean setAttribute(final String name, final Object value) { - boolean result = false; - if (name != null && value != null) { - // update with new value - final Object previous = attributes.put(name, value); - // previous will be null if the attribute didn't exist - // if it did already exist it will equal the previous value - if (previous != null) { - // if the value is a collection, check the elements for modification - if (value instanceof Collection) { - Collection prevCollection = (Collection) previous; - Collection newCollection = (Collection) value; - for (Object newCollectionEntry : newCollection) { - int freq = Collections.frequency(prevCollection, newCollectionEntry); - // first element that does not exist in the previous collection will - // trigger the modified result - if (freq == 0) { - result = true; - break; - } - } - } else if (value instanceof Map) { - Map prevMap = (Map) previous; - Map newMap = (Map) value; - // check key differences first - if (!prevMap.keySet().containsAll(newMap.keySet())) { - result = true; - } else { - // check entries - for (Entry newMapEntry : newMap.entrySet()) { - Object prevValue = prevMap.get(newMapEntry.getKey()); - if (prevValue == null) { - result = true; - break; - } else { - if (!prevValue.equals(newMapEntry.getValue())) { - result = true; - break; - } - } - } - } - } else { - // whether or not the new incoming value is "equal" to the previous value - result = !value.equals(previous); - } - } else { - result = true; - } - } - return result; - } - - /** {@inheritDoc} */ - public boolean setAttributes(Map values) { - attributes.putAll(filterNull(values)); - // put all doesn't return a boolean so we assume all were added - return true; - } - - /** {@inheritDoc} */ - public boolean setAttributes(IAttributeStore values) { - return setAttributes(values.getAttributes()); - } - - /** - * Remove an attribute. - * - * @param name the name of the attribute to remove - * @return true if the attribute was found and removed otherwise false - */ - public boolean removeAttribute(String name) { - if (name != null) { - return (attributes.remove(name) != null); - } - return false; - } - - /** - * Remove all attributes. - */ - public void removeAttributes() { - attributes.clear(); - } - - /** {@inheritDoc} */ - public int size() { - return attributes != null ? attributes.size() : 0; - } - - /** - * Get Boolean attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Boolean getBoolAttribute(String name) { - return (Boolean) getAttribute(name); - } - - /** - * Get Byte attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Byte getByteAttribute(String name) { - return (Byte) getAttribute(name); - } - - /** - * Get Double attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Double getDoubleAttribute(String name) { - return (Double) getAttribute(name); - } - - /** - * Get Integer attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Integer getIntAttribute(String name) { - return (Integer) getAttribute(name); - } - - /** - * Get List attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public List getListAttribute(String name) { - return (List) getAttribute(name); - } - - /** - * Get boolean attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Long getLongAttribute(String name) { - return (Long) getAttribute(name); - } - - /** - * Get Long attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Map getMapAttribute(String name) { - return (Map) getAttribute(name); - } - - /** - * Get Set attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Set getSetAttribute(String name) { - return (Set) getAttribute(name); - } - - /** - * Get Short attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Short getShortAttribute(String name) { - return (Short) getAttribute(name); - } - - /** - * Get String attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public String getStringAttribute(String name) { - return (String) getAttribute(name); - } - - /** - * Allows for reconstruction via CompositeData. - * - * @param cd composite data - * @return AttributeStore class instance - */ - @SuppressWarnings("unchecked") - public static AttributeStore from(CompositeData cd) { - AttributeStore instance = null; - if (cd.containsKey("attributes")) { - Object cn = cd.get("attributes"); - if (cn != null) { - if (cn instanceof IAttributeStore) { - instance = new AttributeStore((IAttributeStore) cn); - } else if (cn instanceof Map) { - instance = new AttributeStore((Map) cn); - } - } else { - instance = new AttributeStore(); - } - } else { - instance = new AttributeStore(); - } - return instance; - } - - @SuppressWarnings("serial") - private final class ConcurrentAttributesMap extends ConcurrentHashMap { - - ConcurrentAttributesMap(int size) { - super(size, 0.75f, 1); - } - - @Override - public V get(Object key) { - log.trace("get key: {}", key); - return super.get(key); - } - - @Override - public V put(K key, V value) { - log.trace("put key: {} value: {}", key, value); - return super.put(key, value); - } - - @Override - public V putIfAbsent(K key, V value) { - log.trace("putIfAbsent key: {} value: {}", key, value); - return super.putIfAbsent(key, value); - } - - @Override - public void putAll(Map m) { - log.trace("putAll map: {}", m); - super.putAll(m); - } - - @Override - public boolean replace(K key, V oldValue, V newValue) { - log.trace("replace key: {} old value: {} new value: {}", new Object[] { key, oldValue, newValue }); - return super.replace(key, oldValue, newValue); - } - - @Override - public V replace(K key, V value) { - log.trace("replace key: {} value: {}", key, value); - return super.replace(key, value); - } - - } - -} diff --git a/src/main/java/org/red5/server/BaseConnection.java b/src/main/java/org/red5/server/BaseConnection.java deleted file mode 100644 index abb8c1393..000000000 --- a/src/main/java/org/red5/server/BaseConnection.java +++ /dev/null @@ -1,583 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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; - -import java.beans.ConstructorProperties; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.commons.lang3.RandomStringUtils; -import org.red5.server.api.IClient; -import org.red5.server.api.IConnection; -import org.red5.server.api.event.IEvent; -import org.red5.server.api.listeners.IConnectionListener; -import org.red5.server.api.scope.IBasicScope; -import org.red5.server.api.scope.IBroadcastScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.scope.Scope; -import org.red5.server.so.SharedObjectScope; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Base abstract class for connections. Adds connection specific functionality like work with clients - * to AttributeStore. - */ -public abstract class BaseConnection extends AttributeStore implements IConnection { - - private static final Logger log = LoggerFactory.getLogger(BaseConnection.class); - - /** - * Connection type - */ - protected final String type; - - /** - * Connection host - */ - protected volatile String host; - - /** - * Connection remote address - */ - protected volatile String remoteAddress; - - /** - * Connection remote addresses - */ - protected volatile List remoteAddresses; - - /** - * Remote port - */ - protected volatile int remotePort; - - /** - * Path of scope client connected to - */ - protected volatile String path; - - /** - * Connection session identifier - */ - protected final String sessionId; - - /** - * Number of read messages - */ - protected AtomicLong readMessages = new AtomicLong(0); - - /** - * Number of written messages - */ - protected AtomicLong writtenMessages = new AtomicLong(0); - - /** - * Number of dropped messages - */ - protected AtomicLong droppedMessages = new AtomicLong(0); - - /** - * Connection params passed from client with NetConnection.connect call - * - * @see NetConnection in Flash Media Server docs (external) - */ - @SuppressWarnings("all") - protected volatile Map params = null; - - /** - * Client bound to connection - */ - protected volatile IClient client; - - /** - * Scope to which this connection belongs - */ - protected volatile Scope scope; - - /** - * Set of basic scopes. The scopes may be of shared object or broadcast stream type. - */ - protected CopyOnWriteArraySet basicScopes = new CopyOnWriteArraySet(); - - /** - * Is the connection closed? - */ - private volatile boolean closed; - - /** - * Listeners - */ - protected CopyOnWriteArrayList connectionListeners = new CopyOnWriteArrayList(); - - /** - * Used to protect mulit-threaded operations on write - */ - private final Semaphore writeLock = new Semaphore(1, true); - - // Support for stream ids - private ThreadLocal streamLocal = new ThreadLocal(); - - /** {@inheritDoc} */ - public int getStreamId() { - return streamLocal.get().intValue(); - } - - /** {@inheritDoc} */ - public void setStreamId(int id) { - streamLocal.set(id); - } - - /** - * Creates a new persistent base connection - */ - @ConstructorProperties(value = { "persistent" }) - public BaseConnection() { - log.debug("New BaseConnection"); - this.type = PERSISTENT; - this.sessionId = RandomStringUtils.randomAlphanumeric(13).toUpperCase(); - log.debug("Generated session id: {}", sessionId); - } - - /** - * Creates a new base connection with the given type. - * - * @param type Connection type - */ - @ConstructorProperties({ "type" }) - public BaseConnection(String type) { - log.debug("New BaseConnection - type: {}", type); - this.type = type; - this.sessionId = RandomStringUtils.randomAlphanumeric(13).toUpperCase(); - log.debug("Generated session id: {}", sessionId); - } - - /** - * Creates a new base connection with the given parameters. - * - * @param type Connection type - * @param host Host - * @param remoteAddress Remote address - * @param remotePort Remote port - * @param path Scope path on server - * @param sessionId Session id - * @param params Params passed from client - */ - @ConstructorProperties({ "type", "host", "remoteAddress", "remotePort", "path", "sessionId" }) - public BaseConnection(String type, String host, String remoteAddress, int remotePort, String path, String sessionId, Map params) { - log.debug("New BaseConnection - type: {} host: {} remoteAddress: {} remotePort: {} path: {} sessionId: {}", new Object[] { type, host, remoteAddress, remotePort, path, - sessionId }); - log.debug("Params: {}", params); - this.type = type; - this.host = host; - this.remoteAddress = remoteAddress; - this.remoteAddresses = new ArrayList(1); - this.remoteAddresses.add(remoteAddress); - this.remoteAddresses = Collections.unmodifiableList(this.remoteAddresses); - this.remotePort = remotePort; - this.path = path; - this.sessionId = sessionId; - this.params = params; - } - - /** {@inheritDoc} */ - public void addListener(IConnectionListener listener) { - this.connectionListeners.add(listener); - } - - /** {@inheritDoc} */ - public void removeListener(IConnectionListener listener) { - this.connectionListeners.remove(listener); - } - - /** - * @return lock for changing state operations - */ - public Semaphore getLock() { - return writeLock; - } - - /** - * Initializes client - * @param client Client bound to connection - */ - public void initialize(IClient client) { - log.debug("initialize - client: {}", client); - if (this.client != null && this.client instanceof Client) { - // unregister old client - ((Client) this.client).unregister(this, false); - } - this.client = client; - if (this.client instanceof Client) { - // register new client - ((Client) this.client).register(this); - } - } - - /** - * - * @return type - */ - public String getType() { - return type; - } - - /** - * - * @return host - */ - public String getHost() { - return host; - } - - /** - * - * @return remote address - */ - public String getRemoteAddress() { - return remoteAddress; - } - - /** - * @return remote address - */ - public List getRemoteAddresses() { - return remoteAddresses; - } - - /** - * - * @return remote port - */ - public int getRemotePort() { - return remotePort; - } - - /** - * - * @return path - */ - public String getPath() { - return path; - } - - /** - * - * @return session id - */ - public String getSessionId() { - return sessionId; - } - - /** - * Return connection parameters - * - * @return connection parameters - */ - public Map getConnectParams() { - return Collections.unmodifiableMap(params); - } - - /** {@inheritDoc} */ - public void setClient(IClient client) { - this.client = client; - } - - /** {@inheritDoc} */ - public IClient getClient() { - return client; - } - - /** - * Check whether connection is alive - * - * @return true if connection is bound to scope, false otherwise - */ - public boolean isConnected() { - log.debug("Connected: {}", (scope != null)); - return scope != null; - } - - /** - * Connect to another scope on server - * @param newScope New scope - * @return true on success, false otherwise - */ - public boolean connect(IScope newScope) { - return connect(newScope, null); - } - - /** - * Connect to another scope on server with given parameters - * @param newScope New scope - * @param params Parameters to connect with - * @return true on success, false otherwise - */ - public boolean connect(IScope newScope, Object[] params) { - if (log.isDebugEnabled()) { - log.debug("Connect Params: {}", params); - if (params != null) { - for (Object e : params) { - log.debug("Param: {}", e); - } - } - } - // disconnect from old scope(s), then reconnect to new scopes. - // this is necessary because there may be an intersection between the hierarchies. - //if (scope != null) { - // scope.disconnect(this); - //} - scope = (Scope) newScope; - return scope.connect(this, params); - } - - /** - * Return the current scope. - * - * @return scope - */ - public IScope getScope() { - return scope; - } - - /** - * Closes connection - */ - public void close() { - if (closed || scope == null) { - log.debug("Close, not connected nothing to do"); - return; - } - closed = true; - log.debug("Close, disconnect from scope, and children"); - try { - // unregister all child scopes first - for (IBasicScope basicScope : basicScopes) { - unregisterBasicScope(basicScope); - } - } catch (Exception err) { - log.error("Error while unregistering basic scopes", err); - } - // disconnect - if (scope != null) { - try { - scope.disconnect(this); - } catch (Exception err) { - log.error("Error while disconnecting from scope: {}. {}", scope, err); - } - scope = null; - } - // unregister client - if (client != null && client instanceof Client) { - ((Client) client).unregister(this); - } - // alert our listeners - if (connectionListeners != null) { - for (IConnectionListener listener : connectionListeners) { - listener.notifyDisconnected(this); - } - connectionListeners.clear(); - connectionListeners = null; - } - } - - /** - * Notified on event - * @param event Event - */ - public void notifyEvent(IEvent event) { - log.debug("Event notify was not handled: {}", event); - } - - /** - * Dispatches event - * @param event Event - */ - public void dispatchEvent(IEvent event) { - log.debug("Event notify was not dispatched: {}", event); - } - - /** - * Handles event - * @param event Event - * @return true if associated scope was able to handle event, false otherwise - */ - public boolean handleEvent(IEvent event) { - return getScope().handleEvent(event); - } - - /** - * - * @return basic scopes - */ - public Iterator getBasicScopes() { - return basicScopes.iterator(); - } - - /** - * Registers basic scope - * @param basicScope Basic scope to register - */ - public void registerBasicScope(IBroadcastScope basicScope) { - basicScopes.add(basicScope); - basicScope.addEventListener(this); - } - - /** - * Registers basic scope - * @param basicScope Basic scope to register - */ - public void registerBasicScope(SharedObjectScope basicScope) { - basicScopes.add(basicScope); - basicScope.addEventListener(this); - } - - /** - * Unregister basic scope - * - * @param basicScope Unregister basic scope - */ - public void unregisterBasicScope(IBasicScope basicScope) { - if (basicScope instanceof IBroadcastScope || basicScope instanceof SharedObjectScope) { - basicScopes.remove(basicScope); - basicScope.removeEventListener(this); - } - } - - /** - * - * @return bytes read - */ - public abstract long getReadBytes(); - - /** - * - * @return bytes written - */ - public abstract long getWrittenBytes(); - - /** - * - * @return messages read - */ - public long getReadMessages() { - return readMessages.get(); - } - - /** - * - * @return messages written - */ - public long getWrittenMessages() { - return writtenMessages.get(); - } - - /** - * - * @return dropped messages - */ - public long getDroppedMessages() { - return droppedMessages.get(); - } - - /** - * Returns whether or not the reader is idle. - * - * @return queued messages - */ - public boolean isReaderIdle() { - return false; - } - - /** - * Returns whether or not the writer is idle. - * - * @return queued messages - */ - public boolean isWriterIdle() { - return false; - } - - /** - * Returns whether or not a connection is closed. - * - * @return true if closed - */ - public boolean isClosed() { - return closed; - } - - /** - * Count of outgoing messages not yet written. - * - * @return pending messages - */ - public long getPendingMessages() { - return 0; - } - - /** - * - * @param streamId the id you want to know about - * @return pending messages for this streamId - */ - public long getPendingVideoMessages(int streamId) { - return 0; - } - - /** {@inheritDoc} */ - public long getClientBytesRead() { - return 0; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = prime * sessionId.hashCode(); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - return sessionId.equals(((BaseConnection) obj).getSessionId()); - } - -} diff --git a/src/main/java/org/red5/server/Client.java b/src/main/java/org/red5/server/Client.java deleted file mode 100644 index 1da86c9d7..000000000 --- a/src/main/java/org/red5/server/Client.java +++ /dev/null @@ -1,380 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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; - -import java.beans.ConstructorProperties; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - -import javax.management.openmbean.CompositeData; - -import org.red5.server.api.IClient; -import org.red5.server.api.IConnection; -import org.red5.server.api.Red5; -import org.red5.server.api.persistence.IPersistable; -import org.red5.server.api.scope.IScope; -import org.red5.server.stream.bandwidth.ClientServerDetection; -import org.red5.server.stream.bandwidth.ServerClientDetection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Client is an abstraction representing user connected to Red5 application. - * Clients are tied to connections and registered in ClientRegistry - */ -public class Client extends AttributeStore implements IClient { - - protected static Logger log = LoggerFactory.getLogger(Client.class); - - /** - * Name of connection attribute holding the permissions. - */ - protected static final String PERMISSIONS = IPersistable.TRANSIENT_PREFIX + "_red5_permissions"; - - /** - * Connections this client is associated with. - */ - protected CopyOnWriteArraySet connections = new CopyOnWriteArraySet(); - - /** - * Creation time as Timestamp - */ - protected long creationTime; - - /** - * Clients identifier - */ - protected String id; - - /** - * Client registry where Client is registered - */ - protected WeakReference registry; - - /** - * Whether or not the bandwidth has been checked. - */ - protected boolean bandwidthChecked; - - /** - * Creates client, sets creation time and registers it in ClientRegistry - * DW: nope, does not currently register it in ClientRegistry! - * - * @param id Client id - * @param registry ClientRegistry - */ - @ConstructorProperties({ "id", "registry" }) - public Client(String id, ClientRegistry registry) { - super(); - this.id = id; - // use a weak reference to prevent any hard-links to the registry - this.registry = new WeakReference(registry); - this.creationTime = System.currentTimeMillis(); - } - - /** - * Disconnects client from Red5 application - */ - public void disconnect() { - log.debug("Disconnect - id: {}", id); - if (connections != null && !connections.isEmpty()) { - log.debug("Closing {} scope connections", connections.size()); - // close all connections held to Red5 by client - for (IConnection con : getConnections()) { - try { - con.close(); - } catch (Exception e) { - // closing a connection calls into application code, so exception possible - log.error("Unexpected exception closing connection {}", e); - } - } - } else { - log.debug("Connection map is empty or null"); - } - // unregister client - removeInstance(); - } - - /** - * Return set of connections for this client - * - * @return Set of connections - */ - public Set getConnections() { - return Collections.unmodifiableSet(connections); - } - - /** - * Return client connections to given scope - * - * @param scope Scope - * @return Set of connections for that scope - */ - public Set getConnections(IScope scope) { - if (scope == null) { - return getConnections(); - } - Set scopeClients = scope.getClients(); - if (scopeClients.contains(this)) { - for (IClient cli : scopeClients) { - if (this.equals(cli)) { - return cli.getConnections(); - } - } - } - return Collections.emptySet(); - } - - /** - * Sets the time at which the client was created. - * - * @param creationTime - */ - public void setCreationTime(long creationTime) { - this.creationTime = creationTime; - } - - /** - * Returns the time at which the client was created. - * - * @return creation time - */ - public long getCreationTime() { - return creationTime; - } - - /** - * Sets the client id - */ - public void setId(String id) { - this.id = id; - } - - /** - * Returns the client id - * @return client id - */ - public String getId() { - return id; - } - - /** - * - * @return scopes on this client - */ - public Collection getScopes() { - Set scopes = new HashSet(); - for (IConnection conn : connections) { - scopes.add(conn.getScope()); - } - return scopes; - } - - /** - * Iterate through the scopes and their attributes. - * Used by JMX - * - * @return list of scope attributes - */ - public List iterateScopeNameList() { - log.debug("iterateScopeNameList called"); - Collection scopes = getScopes(); - log.debug("Scopes: {}", scopes.size()); - List scopeNames = new ArrayList(scopes.size()); - for (IScope scope : scopes) { - log.debug("Client scope: {}", scope); - scopeNames.add(scope.getName()); - if (log.isDebugEnabled()) { - for (Map.Entry entry : scope.getAttributes().entrySet()) { - log.debug("Client scope attr: {} = {}", entry.getKey(), entry.getValue()); - } - } - } - return scopeNames; - } - - /** - * Associate connection with client - * @param conn Connection object - */ - protected void register(IConnection conn) { - log.debug("Registering connection for this client {}", id); - if (conn != null) { - IScope scope = conn.getScope(); - if (scope != null) { - log.debug("Registering for scope: {}", scope); - connections.add(conn); - } else { - log.warn("Clients scope is null. Id: {}", id); - } - } else { - log.warn("Clients connection is null. Id: {}", id); - } - } - - /** - * Removes client-connection association for given connection - * @param conn Connection object - */ - protected void unregister(IConnection conn) { - unregister(conn, true); - } - - /** - * Removes client-connection association for given connection - * @param conn Connection object - * @param deleteIfNoConns Whether to delete this client if it no longer has any connections - */ - protected void unregister(IConnection conn, boolean deleteIfNoConns) { - // remove connection from connected scopes list - connections.remove(conn); - // If client is not connected to any scope any longer then remove - if (deleteIfNoConns && connections.isEmpty()) { - // TODO DW dangerous the way this is called from BaseConnection.initialize(). Could we unexpectedly pop a Client out of the registry? - removeInstance(); - } - } - - /** {@inheritDoc} */ - public boolean isBandwidthChecked() { - return bandwidthChecked; - } - - /** {@inheritDoc} */ - @SuppressWarnings("unchecked") - public Collection getPermissions(IConnection conn) { - Collection result = (Collection) conn.getAttribute(PERMISSIONS); - if (result == null) { - result = Collections.emptySet(); - } - return result; - } - - /** {@inheritDoc} */ - public boolean hasPermission(IConnection conn, String permissionName) { - final Collection permissions = getPermissions(conn); - return permissions.contains(permissionName); - } - - /** {@inheritDoc} */ - public void setPermissions(IConnection conn, Collection permissions) { - if (permissions == null) { - conn.removeAttribute(PERMISSIONS); - } else { - conn.setAttribute(PERMISSIONS, permissions); - } - } - - /** {@inheritDoc} */ - public void checkBandwidth() { - log.debug("Check bandwidth"); - bandwidthChecked = true; - //do something to check the bandwidth, Dan what do you think? - ServerClientDetection detection = new ServerClientDetection(); - detection.checkBandwidth(Red5.getConnectionLocal()); - } - - /** {@inheritDoc} */ - public Map checkBandwidthUp(Object[] params) { - if (log.isDebugEnabled()){ - log.debug("Check bandwidth: {}", Arrays.toString(params)); - } - - bandwidthChecked = true; - //do something to check the bandwidth, Dan what do you think? - ClientServerDetection detection = new ClientServerDetection(); - // if dynamic bw is turned on, we switch to a higher or lower - return detection.checkBandwidth(params); - } - - /** - * Allows for reconstruction via CompositeData. - * - * @param cd composite data - * @return Client class instance - */ - public static Client from(CompositeData cd) { - Client instance = null; - if (cd.containsKey("id")) { - String id = (String) cd.get("id"); - instance = new Client(id, null); - instance.setCreationTime((Long) cd.get("creationTime")); - instance.setAttribute(PERMISSIONS, cd.get(PERMISSIONS)); - } - if (cd.containsKey("attributes")) { - AttributeStore attrs = (AttributeStore) cd.get("attributes"); - instance.setAttributes(attrs); - } - return instance; - } - - /** - * Removes this instance from the client registry. - */ - private void removeInstance() { - // unregister client - ClientRegistry ref = registry.get(); - if (ref != null) { - ref.removeClient(this); - } else { - log.warn("Client registry reference was not accessable, removal failed"); - // TODO: attempt to lookup the registry via the global.clientRegistry - } - } - - @Override - public int hashCode() { - if (id == null) { - return -1; - } - return id.hashCode(); - } - - /** - * Check clients equality by id - * - * @param obj Object to check against - * @return true if clients ids are the same, false otherwise - */ - @Override - public boolean equals(Object obj) { - if (obj instanceof Client) { - return ((Client) obj).getId().equals(id); - } - return false; - } - - /** - * - * @return string representation of client - */ - @Override - public String toString() { - return "Client: " + id; - } - -} diff --git a/src/main/java/org/red5/server/ClientList.java b/src/main/java/org/red5/server/ClientList.java deleted file mode 100644 index 9518119f5..000000000 --- a/src/main/java/org/red5/server/ClientList.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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; - -import java.beans.ConstructorProperties; -import java.lang.ref.WeakReference; -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Client list, implemented using weak references to prevent memory leaks. - * - * @author Paul Gregoire (mondain@gmail.com) - * @param - * type of class - */ -public class ClientList extends AbstractList { - - private CopyOnWriteArrayList> items = new CopyOnWriteArrayList>(); - - @ConstructorProperties(value = { "" }) - public ClientList() { - } - - @ConstructorProperties({"c"}) - public ClientList(Collection c) { - addAll(0, c); - } - - public boolean add(E element) { - return items.add(new WeakReference(element)); - } - - public void add(int index, E element) { - items.add(index, new WeakReference(element)); - } - - @Override - public E remove(int index) { - WeakReference ref = items.remove(index); - return ref.get(); - } - - @Override - public boolean remove(Object o) { - boolean removed = false; - E element = null; - for (WeakReference ref : items) { - element = ref.get(); - if (element != null && element.equals(o)) { - ref.clear(); - removed = true; - break; - } - } - return removed; - } - - @Override - public boolean contains(Object o) { - List list = new ArrayList(); - for (WeakReference ref : items) { - if (ref.get() != null) { - list.add(ref.get()); - } - } - boolean contains = list.contains(o); - list.clear(); - list = null; - return contains; - } - - public int size() { - removeReleased(); - return items.size(); - } - - public E get(int index) { - return (items.get(index)).get(); - } - - private void removeReleased() { - for (WeakReference ref : items) { - if (ref.get() == null) { - items.remove(ref); - } - } - } - -} diff --git a/src/main/java/org/red5/server/ClientRegistry.java b/src/main/java/org/red5/server/ClientRegistry.java deleted file mode 100644 index 024b4e059..000000000 --- a/src/main/java/org/red5/server/ClientRegistry.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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; - -import java.lang.management.ManagementFactory; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.management.StandardMBean; - -import org.apache.commons.lang3.StringUtils; -import org.red5.server.api.IClient; -import org.red5.server.api.IClientRegistry; -import org.red5.server.exception.ClientNotFoundException; -import org.red5.server.exception.ClientRejectedException; -import org.red5.server.jmx.mxbeans.ClientRegistryMXBean; -import org.springframework.jmx.export.annotation.ManagedResource; - -/** - * Registry for clients, wherein clients are mapped by their id. - * - * @author The Red5 Project - */ -@ManagedResource(objectName="org.red5.server:type=ClientRegistry,name=default", description="ClientRegistry") -public class ClientRegistry implements IClientRegistry, ClientRegistryMXBean { - - /** - * Clients map - */ - private ConcurrentMap clients = new ConcurrentHashMap(6, 0.9f, 2); - - /** - * Next client id - */ - private AtomicInteger nextId = new AtomicInteger(0); - - /** - * The identifier for this client registry - */ - private String name; - - public ClientRegistry() { - } - - //allows for setting a "name" to be used with jmx for lookup - public ClientRegistry(String name) { - this.name = name; - if (StringUtils.isNotBlank(this.name)) { - try { - MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); - ObjectName oName = new ObjectName("org.red5.server:type=ClientRegistry,name=" + name); - mbeanServer.registerMBean(new StandardMBean(this, ClientRegistryMXBean.class, true), oName); - } catch (Exception e) { - //log.warn("Error on jmx registration", e); - } - } - } - - /** - * Add client to registry - * @param client Client to add - */ - public void addClient(IClient client) { - addClient(client.getId(), client); - } - - /** - * Add the client to the registry - */ - private void addClient(String id, IClient client) { - //check to see if the id already exists first - if (!hasClient(id)) { - clients.put(id, client); - } else { - // DW the Client object is meant to be unifying connections from a remote user. But currently the only case we - // specify this currently is when we use a remoting session. So we actually just create an arbitrary id, which means - // RTMP connections from same user are not combined. - //get the next available client id - String newId = nextId(); - //update the client - client.setId(newId); - //add the client to the list - addClient(newId, client); - } - } - - public Client getClient(String id) throws ClientNotFoundException { - Client result = (Client) clients.get(id); - if (result == null) { - throw new ClientNotFoundException(id); - } - return result; - } - - /** - * Returns a list of Clients. - */ - public ClientList getClientList() { - ClientList list = new ClientList(); - for (IClient c : clients.values()) { - list.add((Client) c); - } - return list; - } - - /** - * Check if client registry contains clients. - * - * @return True if clients exist, otherwise False - */ - protected boolean hasClients() { - return !clients.isEmpty(); - } - - /** - * Return collection of clients - * @return Collection of clients - */ - @SuppressWarnings("unchecked") - protected Collection getClients() { - if (!hasClients()) { - // Avoid creating new Collection object if no clients exist. - return Collections.EMPTY_SET; - } - return Collections.unmodifiableCollection(clients.values()); - } - - /** - * Check whether registry has client with given id - * - * @param id Client id - * @return true if client with given id was register with this registry, false otherwise - */ - public boolean hasClient(String id) { - if (id == null) { - // null ids are not supported - return false; - } - return clients.containsKey(id); - } - - /** - * Return client by id - * - * @param id Client id - * @return Client object associated with given id - * @throws ClientNotFoundException if we can't find client - */ - public IClient lookupClient(String id) throws ClientNotFoundException { - return getClient(id); - } - - /** - * Return client from next id with given params - * - * @param params Client params - * @return Client object - * @throws ClientNotFoundException if client not found - * @throws ClientRejectedException if client rejected - */ - public IClient newClient(Object[] params) throws ClientNotFoundException, ClientRejectedException { - // derive client id from the connection params or use next - String id = nextId(); - IClient client = new Client(id, this); - addClient(id, client); - return client; - } - - /** - * Return next client id - * @return Next client id - */ - public String nextId() { - //when we reach max int, reset to zero - if (nextId.get() == Integer.MAX_VALUE) { - nextId.set(0); - } - return String.format("%s", nextId.getAndIncrement()); - } - - /** - * Return previous client id - * @return Previous client id - */ - public String previousId() { - return String.format("%s", nextId.get()); - } - - /** - * Removes client from registry - * @param client Client to remove - */ - protected void removeClient(IClient client) { - clients.remove(client.getId()); - } - -} diff --git a/src/main/java/org/red5/server/Server.java b/src/main/java/org/red5/server/Server.java deleted file mode 100644 index 1b360c2f7..000000000 --- a/src/main/java/org/red5/server/Server.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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; - -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArraySet; - -import org.red5.server.api.IConnection; -import org.red5.server.api.IServer; -import org.red5.server.api.listeners.IConnectionListener; -import org.red5.server.api.listeners.IScopeListener; -import org.red5.server.api.scheduling.IScheduledJob; -import org.red5.server.api.scheduling.ISchedulingService; -import org.red5.server.api.scope.IGlobalScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.scheduling.QuartzSchedulingService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.core.style.ToStringCreator; - -/** - * Red5 server core class implementation. - */ -public class Server implements IServer, ApplicationContextAware, InitializingBean, DisposableBean { - - protected static Logger log = LoggerFactory.getLogger(Server.class); - - /** - * Service used to provide notifications. - */ - private static QuartzSchedulingService schedulingService; - - /** - * List of global scopes - */ - protected ConcurrentMap globals = new ConcurrentHashMap(1, 0.9f, 1); - - /** - * Mappings - */ - protected ConcurrentMap mapping = new ConcurrentHashMap(32, 0.9f, 8); - - /** - * Spring application context - */ - protected ApplicationContext applicationContext; - - /** - * Constant for slash - */ - protected static final String SLASH = "/"; - - /** - * Constant for empty string - */ - protected static final String EMPTY = ""; - - public Set scopeListeners = new CopyOnWriteArraySet(); - - public Set connectionListeners = new CopyOnWriteArraySet(); - - /** - * Setter for Spring application context - * - * @param applicationContext Application context - */ - public void setApplicationContext(ApplicationContext applicationContext) { - log.debug("Setting application context"); - this.applicationContext = applicationContext; - } - - /** - * Initialization section. - */ - public void afterPropertiesSet() throws Exception { - Server.schedulingService = (QuartzSchedulingService) applicationContext.getBean("schedulingService"); - } - - /** - * Destruction section. - */ - public void destroy() throws Exception { - } - - /** - * Return scope key. Scope key consists of host name concatenated with - * context path by slash symbol - * - * @param hostName Host name - * @param contextPath Context path - * @return Scope key as string - */ - protected String getKey(String hostName, String contextPath) { - return String.format("%s/%s", (hostName == null ? EMPTY : hostName), (contextPath == null ? EMPTY : contextPath)); - } - - /** - * Does global scope lookup for host name and context path - * - * @param hostName Host name - * @param contextPath Context path - * @return Global scope - */ - public IGlobalScope lookupGlobal(String hostName, String contextPath) { - log.trace("{}", this); - log.debug("Lookup global scope - host name: {} context path: {}", hostName, contextPath); - // Init mappings key - String key = getKey(hostName, contextPath); - // If context path contains slashes get complex key and look for it in mappings - while (contextPath.indexOf(SLASH) != -1) { - key = getKey(hostName, contextPath); - log.trace("Check: {}", key); - String globalName = mapping.get(key); - if (globalName != null) { - return getGlobal(globalName); - } - final int slashIndex = contextPath.lastIndexOf(SLASH); - // Context path is substring from the beginning and till last slash index - contextPath = contextPath.substring(0, slashIndex); - } - // Get global scope key - key = getKey(hostName, contextPath); - log.trace("Check host and path: {}", key); - // Look up for global scope switching keys if still not found - String globalName = mapping.get(key); - if (globalName != null) { - return getGlobal(globalName); - } - key = getKey(EMPTY, contextPath); - log.trace("Check wildcard host with path: {}", key); - globalName = mapping.get(key); - if (globalName != null) { - return getGlobal(globalName); - } - key = getKey(hostName, EMPTY); - log.trace("Check host with no path: {}", key); - globalName = mapping.get(key); - if (globalName != null) { - return getGlobal(globalName); - } - key = getKey(EMPTY, EMPTY); - log.trace("Check default host, default path: {}", key); - return getGlobal(mapping.get(key)); - } - - /** - * Return global scope by name - * - * @param name Global scope name - * @return Global scope - */ - public IGlobalScope getGlobal(String name) { - if (name == null) { - return null; - } - return globals.get(name); - } - - /** - * Register global scope - * - * @param scope Global scope to register - */ - public void registerGlobal(IGlobalScope scope) { - log.trace("Registering global scope: {}", scope.getName(), scope); - globals.put(scope.getName(), scope); - } - - /** - * Map key (host + / + context path) and global scope name - * - * @param hostName Host name - * @param contextPath Context path - * @param globalName Global scope name - * @return true if mapping was added, false if already exist - */ - public boolean addMapping(String hostName, String contextPath, String globalName) { - log.info("Add mapping global: {} host: {} context: {}", new Object[] { globalName, hostName, contextPath }); - final String key = getKey(hostName, contextPath); - log.debug("Add mapping: {} => {}", key, globalName); - return (mapping.putIfAbsent(key, globalName) == null); - } - - /** - * Remove mapping with given key - * - * @param hostName Host name - * @param contextPath Context path - * @return true if mapping was removed, false if key doesn't exist - */ - public boolean removeMapping(String hostName, String contextPath) { - log.info("Remove mapping host: {} context: {}", hostName, contextPath); - final String key = getKey(hostName, contextPath); - log.debug("Remove mapping: {}", key); - return (mapping.remove(key) != null); - } - - /** - * Remove all mappings with given context path - * - * @param contextPath Context path - * @return true if mapping was removed, false if key doesn't exist - */ - public boolean removeMapping(String contextPath) { - log.info("Remove mapping context: {}", contextPath); - final String key = getKey("", contextPath); - log.debug("Remove mapping: {}", key); - return (mapping.remove(key) != null); - } - - /** - * Return mapping - * - * @return Map of "scope key / scope name" pairs - */ - public Map getMappingTable() { - return mapping; - } - - /** - * Return global scope names set iterator - * - * @return Iterator - */ - public Iterator getGlobalNames() { - return globals.keySet().iterator(); - } - - /** - * Return global scopes set iterator - * - * @return Iterator - */ - public Iterator getGlobalScopes() { - return globals.values().iterator(); - } - - /** - * String representation of server - * - * @return String representation of server - */ - @Override - public String toString() { - return new ToStringCreator(this).append(mapping).toString(); - } - - /** {@inheritDoc} */ - public void addListener(IScopeListener listener) { - scopeListeners.add(listener); - } - - /** {@inheritDoc} */ - public void addListener(IConnectionListener listener) { - connectionListeners.add(listener); - } - - /** {@inheritDoc} */ - public void removeListener(IScopeListener listener) { - scopeListeners.remove(listener); - } - - /** {@inheritDoc} */ - public void removeListener(IConnectionListener listener) { - connectionListeners.remove(listener); - } - - /** - * Notify listeners about a newly created scope. - * - * @param scope - * the scope that was created - */ - public void notifyScopeCreated(IScope scope) { - schedulingService.addScheduledOnceJob(10, new ScopeCreatedJob(scope)); - } - - /** - * Notify listeners that a scope was removed. - * - * @param scope - * the scope that was removed - */ - public void notifyScopeRemoved(IScope scope) { - schedulingService.addScheduledOnceJob(10, new ScopeRemovedJob(scope)); - } - - /** - * Notify listeners that a new connection was established. - * - * @param conn - * the new connection - */ - public void notifyConnected(IConnection conn) { - schedulingService.addScheduledOnceJob(10, new ConnectedJob(conn)); - } - - /** - * Notify listeners that a connection was disconnected. - * - * @param conn - * the disconnected connection - */ - public void notifyDisconnected(final IConnection conn) { - schedulingService.addScheduledOnceJob(10, new DisconnectedJob(conn)); - } - - /** - * Used to indicate a scope was created. - */ - private final class ScopeCreatedJob implements IScheduledJob { - - private IScope scope; - - ScopeCreatedJob(IScope scope) { - this.scope = scope; - } - - public void execute(ISchedulingService service) { - for (IScopeListener listener : scopeListeners) { - listener.notifyScopeCreated(scope); - } - } - - } - - /** - * Used to indicate a scope was removed. - */ - private final class ScopeRemovedJob implements IScheduledJob { - - private IScope scope; - - ScopeRemovedJob(IScope scope) { - this.scope = scope; - } - - public void execute(ISchedulingService service) { - for (IScopeListener listener : scopeListeners) { - listener.notifyScopeRemoved(scope); - } - } - - } - - private final class ConnectedJob implements IScheduledJob { - - private IConnection conn; - - ConnectedJob(IConnection conn) { - this.conn = conn; - } - - public void execute(ISchedulingService service) { - for (IConnectionListener listener : connectionListeners) { - listener.notifyConnected(conn); - } - } - - } - - private final class DisconnectedJob implements IScheduledJob { - - private IConnection conn; - - DisconnectedJob(IConnection conn) { - this.conn = conn; - } - - public void execute(ISchedulingService service) { - for (IConnectionListener listener : connectionListeners) { - listener.notifyDisconnected(conn); - } - } - - } - -} diff --git a/src/main/java/org/red5/server/adapter/AbstractScopeAdapter.java b/src/main/java/org/red5/server/adapter/AbstractScopeAdapter.java deleted file mode 100644 index 0645f3793..000000000 --- a/src/main/java/org/red5/server/adapter/AbstractScopeAdapter.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.adapter; - -import java.util.Map; - -import org.red5.server.api.IClient; -import org.red5.server.api.IConnection; -import org.red5.server.api.Red5; -import org.red5.server.api.event.IEvent; -import org.red5.server.api.scope.IBasicScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeHandler; -import org.red5.server.api.service.IServiceCall; - -/** - * Base scope handler implementation. Meant to be subclassed. - */ -public abstract class AbstractScopeAdapter implements IScopeHandler { - - //private static Logger log = LoggerFactory.getLogger(AbstractScopeAdapter.class); - - /** - * Can start flag. - * true if scope is ready to be activated, false otherwise - */ - private boolean canStart = true; - - /** - * Can connect flag. - * true if connections to scope are allowed, false otherwise - */ - private boolean canConnect; - - /** - * Can join flag. - * true if scope may be joined by users, false otherwise - */ - private boolean canJoin = true; - - /** - * Can call service flag. - * true if remote service calls are allowed for the scope, false otherwise - */ - private boolean canCallService = true; - - /** - * Can add child scope flag. true if scope is allowed to add child scopes, false otherwise - */ - private boolean canAddChildScope = true; - - /** - * Can handle event flag. - * true if events handling is allowed, false otherwise - */ - private boolean canHandleEvent = true; - - /** - * Setter for can start flag. - * - * @param canStart true if scope is ready to be activated, false otherwise - */ - public void setCanStart(boolean canStart) { - this.canStart = canStart; - } - - /** - * Setter for can call service flag - * - * @param canCallService true if remote service calls are allowed for the scope, false otherwise - */ - public void setCanCallService(boolean canCallService) { - //log.trace("setCanCallService: {}", canCallService); - this.canCallService = canCallService; - } - - /** - * Setter for can connect flag - * - * @param canConnect true if connections to scope are allowed, false otherwise - */ - public void setCanConnect(boolean canConnect) { - this.canConnect = canConnect; - } - - /** - * Setter for 'can join' flag - * - * @param canJoin true if scope may be joined by users, false otherwise - */ - public void setJoin(boolean canJoin) { - this.canJoin = canJoin; - } - - /** {@inheritDoc} */ - public boolean start(IScope scope) { - return canStart; - } - - /** {@inheritDoc} */ - public void stop(IScope scope) { - // nothing - } - - /** {@inheritDoc} */ - public boolean connect(IConnection conn, IScope scope, Object[] params) { - return canConnect; - } - - /** {@inheritDoc} */ - public void disconnect(IConnection conn, IScope scope) { - // nothing - } - - /** {@inheritDoc} */ - public boolean join(IClient client, IScope scope) { - return canJoin; - } - - /** {@inheritDoc} */ - public void leave(IClient client, IScope scope) { - // nothing - } - - /** {@inheritDoc} */ - public boolean serviceCall(IConnection conn, IServiceCall call) { - //log.trace("serviceCall - canCallService: {} scope: {} method: {}", canCallService, conn.getScope().getName(), call.getServiceMethodName()); - return canCallService; - } - - /** {@inheritDoc} */ - public boolean addChildScope(IBasicScope scope) { - return canAddChildScope; - } - - /** {@inheritDoc} */ - public void removeChildScope(IBasicScope scope) { - } - - /** {@inheritDoc} */ - public boolean handleEvent(IEvent event) { - return canHandleEvent; - } - - /** - * Calls the checkBandwidth method on the current client. - * @param o Object passed from Flash, not used at the moment - */ - public void checkBandwidth(Object o) { - //Incoming object should be null - IClient client = Red5.getConnectionLocal().getClient(); - if (client != null) { - client.checkBandwidth(); - } - } - - /** - * Calls the checkBandwidthUp method on the current client. - * @param params Object passed from Flash - */ - public Map checkBandwidthUp(Object[] params) { - //Incoming object should be null - IClient client = Red5.getConnectionLocal().getClient(); - if (client != null) { - return client.checkBandwidthUp(params); - } - return null; - } - -} diff --git a/src/main/java/org/red5/server/adapter/IApplication.java b/src/main/java/org/red5/server/adapter/IApplication.java deleted file mode 100644 index 9a6ae005f..000000000 --- a/src/main/java/org/red5/server/adapter/IApplication.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.adapter; - -import org.red5.server.api.IClient; -import org.red5.server.api.IConnection; -import org.red5.server.api.scope.IScope; - -/** - * IApplication provides lifecycle methods that most communication applications - * will use. This interface defines the methods that are called by Red5 through - * an applications life. It is suggested that you NOT implement this interface yourself, but - * instead you should subclass {@link MultiThreadedApplicationAdapter} or {@link ApplicationAdapter}. - * - * @author Dominick Accattato - */ -public interface IApplication { - - /** - * Called once when application or room starts - * - * @param app Application or room level scope. See - * {@link org.red5.server.api.scope.IScope} for details - * @return true continues application run, false - * terminates - */ - public boolean appStart(IScope app); - - /** - * Called per each client connect - * - * @param conn Connection object used to provide basic connection methods. - * See {@link org.red5.server.api.IConnection} - * @param params List of params sent from client with NetConnection.connect - * call - * @return true accepts the connection, false - * rejects it - */ - public boolean appConnect(IConnection conn, Object[] params); - - /** - * Called every time client joins app level scope - * - * @param client Client object - * @param app Scope object - * @return true accepts the client, false - * rejects it - */ - public boolean appJoin(IClient client, IScope app); - - /** - * Called every time client disconnects from the application - * - * @param conn Connection object See {@link org.red5.server.api.IConnection} - */ - public void appDisconnect(IConnection conn); - - /** - * Called every time client leaves the application scope - * - * @param client Client object - * @param app Scope object - */ - public void appLeave(IClient client, IScope app); - - /** - * Called on application stop - * - * @param app Scope object - */ - public void appStop(IScope app); - - /** - * Called on application room start - * - * @param room Scope object - * @return true if scope can be started, false - * otherwise - */ - public boolean roomStart(IScope room); - - /** - * Called every time client connects to the room - * - * @param conn Connection object - * @param params List of params sent from client with NetConnection.connect - * call - * @return true accepts the connection, false - * rejects it - */ - public boolean roomConnect(IConnection conn, Object[] params); - - /** - * Called when user joins room scope - * - * @param client Client object - * @param room Scope object - * @return true accepts the client, false - * rejects it - */ - public boolean roomJoin(IClient client, IScope room); - - /** - * Called when client disconnects from room scope - * - * @param conn Connection object used to provide basic connection methods. - * See {@link org.red5.server.api.IConnection} - */ - public void roomDisconnect(IConnection conn); - - /** - * Called when user leaves room scope - * - * @param client Client object - * @param room Scope object - */ - public void roomLeave(IClient client, IScope room); - - /** - * Called on room scope stop - * - * @param room Scope object - */ - public void roomStop(IScope room); - -} diff --git a/src/main/java/org/red5/server/adapter/StatefulScopeWrappingAdapter.java b/src/main/java/org/red5/server/adapter/StatefulScopeWrappingAdapter.java deleted file mode 100644 index 33d998db1..000000000 --- a/src/main/java/org/red5/server/adapter/StatefulScopeWrappingAdapter.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.adapter; - -import java.io.IOException; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.red5.server.api.IAttributeStore; -import org.red5.server.api.IClient; -import org.red5.server.api.IConnection; -import org.red5.server.api.IContext; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeAware; -import org.red5.server.plugin.PluginDescriptor; -import org.springframework.core.io.Resource; - -/** - * StatefulScopeWrappingAdapter class wraps stateful IScope functionality. That - * is, it has attributes that you can work with, subscopes, associated resources - * and connections. - * - */ -public class StatefulScopeWrappingAdapter extends AbstractScopeAdapter implements IScopeAware, IAttributeStore { - - //private static Logger log = LoggerFactory.getLogger(StatefulScopeWrappingAdapter.class); - - /** - * Wrapped scope - */ - protected IScope scope; - - /** - * List of plug-in descriptors - */ - protected List plugins; - - /** {@inheritDoc} */ - public void setScope(IScope scope) { - //log.trace("setScope: {}", scope.getName()); - this.scope = scope; - } - - /** - * Getter for wrapped scope - * - * @return Wrapped scope - */ - public IScope getScope() { - //log.trace("getScope: {}", scope.getName()); - return scope; - } - - /** - * Returns any plug-ins descriptors added - * - * @return plug-in descriptor list - */ - public List getPlugins() { - return plugins; - } - - /** - * Adds a list of plug-in descriptors - * - * @param plugins - */ - public void setPlugins(List plugins) { - this.plugins = plugins; - } - - /** {@inheritDoc} */ - public Object getAttribute(String name) { - return scope.getAttribute(name); - } - - /** {@inheritDoc} */ - public Object getAttribute(String name, Object defaultValue) { - Object value = scope.getAttribute(name); - if (value == null) { - value = defaultValue; - } - return value; - } - - /** {@inheritDoc} */ - public Set getAttributeNames() { - return scope.getAttributeNames(); - } - - /** - * Wrapper for Scope#getAttributes - * @return Scope attributes map - */ - public Map getAttributes() { - return scope.getAttributes(); - } - - /** {@inheritDoc} */ - public boolean hasAttribute(String name) { - return scope.hasAttribute(name); - } - - /** {@inheritDoc} */ - public boolean removeAttribute(String name) { - return scope.removeAttribute(name); - } - - /** {@inheritDoc} */ - public void removeAttributes() { - Set names = scope.getAttributeNames(); - for (String name : names) { - scope.removeAttribute(name); - } - } - - /** {@inheritDoc} */ - public int size() { - return scope != null ? scope.getAttributeNames().size() : 0; - } - - /** {@inheritDoc} */ - public boolean setAttribute(String name, Object value) { - return scope.setAttribute(name, value); - } - - /** {@inheritDoc} */ - public boolean setAttributes(IAttributeStore attributes) { - int successes = 0; - for (Map.Entry entry : attributes.getAttributes().entrySet()) { - if (scope.setAttribute(entry.getKey(), entry.getValue())) { - successes++; - } - } - // expect every value to have been added - return (successes == attributes.size()); - } - - /** {@inheritDoc} */ - public boolean setAttributes(Map attributes) { - int successes = 0; - for (Map.Entry entry : attributes.entrySet()) { - if (scope.setAttribute(entry.getKey(), entry.getValue())) { - successes++; - } - } - // expect every value to have been added - return (successes == attributes.size()); - } - - /** - * Creates child scope - * @param name Child scope name - * @return true on success, false otherwise - */ - public boolean createChildScope(String name) { - if (!scope.hasChildScope(name)) { - return scope.createChildScope(name); - } - return false; - } - - /** - * Return child scope - * @param name Child scope name - * @return Child scope with given name - */ - public IScope getChildScope(String name) { - return scope.getScope(name); - } - - /** - * Iterator for child scope names - * - * @return collection of child scope names - */ - public Set getChildScopeNames() { - return scope.getScopeNames(); - } - - /** - * Getter for set of clients - * - * @return Set of clients - */ - public Set getClients() { - return scope.getClients(); - } - - /** - * Returns all connections in the scope - * - * @return Connections - */ - @SuppressWarnings("deprecation") - public Collection> getConnections() { - return scope.getConnections(); - } - - /** - * Returns all connections for a given client - * - * @param client - * @return Connections - */ - @SuppressWarnings("deprecation") - public Set lookupConnections(IClient client) { - return scope.lookupConnections(client); - } - - /** - * Getter for context - * - * @return Value for context - */ - public IContext getContext() { - return scope.getContext(); - } - - /** - * Getter for depth - * - * @return Value for depth - */ - public int getDepth() { - return scope.getDepth(); - } - - /** - * Getter for name - * - * @return Value for name - */ - public String getName() { - return scope.getName(); - } - - /** - * Return parent scope - * - * @return Parent scope - */ - public IScope getParent() { - return scope.getParent(); - } - - /** - * Getter for stateful scope path - * - * @return Value for path - */ - public String getPath() { - return scope.getPath(); - } - - /** - * Whether this scope has a child scope with given name - * @param name Child scope name - * @return true if it does have it, false otherwise - */ - public boolean hasChildScope(String name) { - return scope.hasChildScope(name); - } - - /** - * If this scope has a parent - * @return true if this scope has a parent scope, false otherwise - */ - public boolean hasParent() { - return scope.hasParent(); - } - - /** - * Returns array of resources (as Spring core Resource class instances) - * - * @param pattern Resource pattern - * @return Returns array of resources - * @throws IOException I/O exception - */ - public Resource[] getResources(String pattern) throws IOException { - return scope.getResources(pattern); - } - - /** - * Return resource by name - * @param path Resource name - * @return Resource with given name - */ - public Resource getResource(String path) { - return scope.getResource(path); - } - -} diff --git a/src/main/java/org/red5/server/api/IAttributeStore.java b/src/main/java/org/red5/server/api/IAttributeStore.java deleted file mode 100644 index e02d4ef0c..000000000 --- a/src/main/java/org/red5/server/api/IAttributeStore.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api; - -import java.util.Map; -import java.util.Set; - -import org.red5.server.jmx.mxbeans.AttributeStoreMXBean; - -/** - * Base interface for all API objects with attributes - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - */ -public interface IAttributeStore extends AttributeStoreMXBean { - - /** - * Get the attribute names. The resulting set will be read-only. - * - * @return set containing all attribute names - */ - public Set getAttributeNames(); - - /** - * Get the attributes. The resulting map will be read-only. - * - * @return map containing all attributes - */ - public Map getAttributes(); - - /** - * Set an attribute on this object. - * - * @param name the name of the attribute to change - * @param value the new value of the attribute - * @return true if the attribute value changed otherwise false - */ - public boolean setAttribute(String name, Object value); - - /** - * Set multiple attributes on this object. - * - * @param values the attributes to set - * @return true if the attribute values changed otherwise false - */ - public boolean setAttributes(Map values); - - /** - * Set multiple attributes on this object. - * - * @param values the attributes to set - * @return true if the attribute values changed otherwise false - */ - public boolean setAttributes(IAttributeStore values); - - /** - * Return the value for a given attribute. - * - * @param name the name of the attribute to get - * @return the attribute value or null if the attribute doesn't exist - */ - public Object getAttribute(String name); - - /** - * Return the value for a given attribute and set it if it doesn't exist. - * - *

- * This is a utility function that internally performs the following code: - *

- * - * if (!hasAttribute(name)) setAttribute(name, defaultValue);
- * return getAttribute(name);
- *
- *

- *

- * - * @param name the name of the attribute to get - * @param defaultValue the value of the attribute to set if the attribute doesn't - * exist - * @return the attribute value - */ - public Object getAttribute(String name, Object defaultValue); - - /** - * Check the object has an attribute. - * - * @param name the name of the attribute to check - * @return true if the attribute exists otherwise false - */ - public boolean hasAttribute(String name); - - /** - * Remove an attribute. - * - * @param name the name of the attribute to remove - * @return true if the attribute was found and removed otherwise false - */ - public boolean removeAttribute(String name); - - /** - * Remove all attributes. - */ - public void removeAttributes(); - - /** - * Size of the attribute store. - * - * @return count of attributes - */ - public int size(); - -} diff --git a/src/main/java/org/red5/server/api/ICastingAttributeStore.java b/src/main/java/org/red5/server/api/ICastingAttributeStore.java deleted file mode 100644 index d616d8ab8..000000000 --- a/src/main/java/org/red5/server/api/ICastingAttributeStore.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Attribute storage with automatic object casting support. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface ICastingAttributeStore extends IAttributeStore { - - /** - * Get Boolean attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Boolean getBoolAttribute(String name); - - /** - * Get Byte attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Byte getByteAttribute(String name); - - /** - * Get Double attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Double getDoubleAttribute(String name); - - /** - * Get Integer attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Integer getIntAttribute(String name); - - /** - * Get List attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public List getListAttribute(String name); - - /** - * Get boolean attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Long getLongAttribute(String name); - - /** - * Get Long attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Map getMapAttribute(String name); - - /** - * Get Set attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Set getSetAttribute(String name); - - /** - * Get Short attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public Short getShortAttribute(String name); - - /** - * Get String attribute by name - * - * @param name Attribute name - * @return Attribute - */ - public String getStringAttribute(String name); - -} diff --git a/src/main/java/org/red5/server/api/IClient.java b/src/main/java/org/red5/server/api/IClient.java deleted file mode 100644 index 80c3fd37e..000000000 --- a/src/main/java/org/red5/server/api/IClient.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -import org.red5.server.adapter.IApplication; -import org.red5.server.api.scope.IScope; - -/** - * The client object represents a single client. One client may have multiple - * connections to different scopes on the same host. In some ways the client - * object is like a HTTP session. You can create IClient objects with - * {@link IClientRegistry#newClient(Object[])} - * - * - * NOTE: I removed session, since client serves the same purpose as a client - * with attributes - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - */ -public interface IClient extends IAttributeStore { - - /** - * The key used to store the client object in a http session. - */ - public static final String ID = "red5.client"; - - /** - * Sets the clients id - * @param id client id - */ - public void setId(String id); - - /** - * Get the unique ID for this client. This will be generated by the server - * if not passed upon connection from client-side Flex/Flash app. To assign a custom ID to the client use - * params object of - * {@link IApplication#appConnect(IConnection, Object[])} method, that - * contains 2nd all the rest values you pass to - * NetConnection.connect method. - * - * Example: - * - * At client side: - * NetConnection.connect( "http://localhost/killerapp/", "user123" ); - * - * then at server side: - * public boolean appConnect( IConnection connection, Object[] params ){
- * try { - * connection.getClient().setId( (String) params[0] ); - * } catch(Exception e){
- * log.error("{}", e); - * } - * } - *
- * - * @return client id - */ - public String getId(); - - /** - * Get the creation time for this client object. - * - * @return Creation time in milliseconds - */ - public long getCreationTime(); - - /** - * Get a set of scopes the client is connected to. - * - * @return Set of scopes - */ - public Collection getScopes(); - - /** - * Get a set of connections. - * - * @return Set of connections - */ - public Set getConnections(); - - /** - * Get a set of connections of a given scope. - * - * @param scope scope to get connections for - * @return Set of connections to the passed scope - */ - public Set getConnections(IScope scope); - - /** - * Closes all the connections. - */ - public void disconnect(); - - /** - * Set the permissions for this client in a given context. - * - * @param conn Connection specifying the context to set the permissions for. - * @param permissions Permissions the client has in this context or null for no permissions. - */ - public void setPermissions(IConnection conn, Collection permissions); - - /** - * Return the permissions in a given context. - * - * @param conn Connection specifying the context to get the permissions for. - * @return Permission names. - */ - public Collection getPermissions(IConnection conn); - - /** - * Check if the client has a permission in the given context. - * - * @param conn Connection specifying the context to check the permissions for. - * @param permissionName Name of the permission to check. - * @return true if the client has the permission, otherwise false - */ - public boolean hasPermission(IConnection conn, String permissionName); - - /** - * Performs a bandwidth checking routine. - * Information may be found here: http://www.adobe.com/devnet/flashmediaserver/articles/dynamic_stream_switching_04.html - */ - public void checkBandwidth(); - - /** - * Performs a bandwidth checking callback for the client. - * Information may be found here: http://www.adobe.com/devnet/flashmediaserver/articles/dynamic_stream_switching_04.html - */ - public Map checkBandwidthUp(Object[] params); - - /** - * Returns whether or not a bandwidth check has been requested. - * @return true if requested and false otherwise - */ - public boolean isBandwidthChecked(); - -} diff --git a/src/main/java/org/red5/server/api/IClientRegistry.java b/src/main/java/org/red5/server/api/IClientRegistry.java deleted file mode 100644 index 2ff45a8f0..000000000 --- a/src/main/java/org/red5/server/api/IClientRegistry.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api; - -import org.red5.server.exception.ClientNotFoundException; -import org.red5.server.exception.ClientRejectedException; - -/** - * Provides a registry of client objects. - * You can lookup a client by its client id / session id using lookupClient method. - * This interface implementations also create new client objects from given params, usually - * passed from client-side Flex/Flash application upon initial connection. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - */ -public interface IClientRegistry { - - /** - * Check if a client with a given id exists. - * - * @param id the id of the client to check for - * @return true if the client exists, false otherwise - */ - public boolean hasClient(String id); - - /** - * Create a new client client object from connection params. - * - * @param params the parameters the client passed during connection - * @return the new client - * @throws ClientNotFoundException no client could be created from the passed parameters - * @throws ClientRejectedException the client is not allowed to connect - */ - public IClient newClient(Object[] params) throws ClientNotFoundException, ClientRejectedException; - - /** - * Return an existing client from a client id. - * - * @param id the id of the client to return - * @return the client object - * @throws ClientNotFoundException no client with the passed id exists - */ - public IClient lookupClient(String id) throws ClientNotFoundException; - - /** - * Adds a client to the registry. - * - * @param client - */ - public void addClient(IClient client); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/IConnection.java b/src/main/java/org/red5/server/api/IConnection.java deleted file mode 100644 index 6f9784a08..000000000 --- a/src/main/java/org/red5/server/api/IConnection.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.red5.server.api.listeners.IConnectionListener; -import org.red5.server.api.scope.IBasicScope; -import org.red5.server.api.scope.IScope; - -/** - * The connection object. - * - * Each connection has an associated client and scope. Connections may be - * persistent, polling, or transient. The aim of this interface is to provide - * basic connection methods shared between different types of connections - * - * Future subclasses: RTMPConnection, RemotingConnection, AJAXConnection, - * HttpConnection, etc - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - */ -public interface IConnection extends ICoreObject, ICastingAttributeStore { - - /** - * AMF version types, either AMF0 or AMF3. - */ - public static enum Encoding { - AMF0, - AMF3 - }; - - /** - * Persistent connection type, eg RTMP. - */ - public static final String PERSISTENT = "persistent"; - - /** - * Polling connection type, eg RTMPT. - */ - public static final String POLLING = "polling"; - - /** - * Transient connection type, eg Remoting, HTTP, etc. - */ - public static final String TRANSIENT = "transient"; - - /** - * Get the connection type. - * - * @return string containing one of connection types - */ - public String getType(); // PERSISTENT | POLLING | TRANSIENT - - /** - * Get the object encoding (AMF version) for this connection. - * - * @return the used encoding. - */ - public Encoding getEncoding(); - - /** - * Initialize the connection. - * @param client Client object associated with connection - */ - public void initialize(IClient client); - - /** - * Try to connect to the scope. - * @return true on success, false otherwise - * @param scope Scope object - */ - public boolean connect(IScope scope); - - /** - * Try to connect to the scope with a list of connection parameters. - * @param params Connections parameters - * @return true on success, false otherwise - * @param scope Scope object - */ - public boolean connect(IScope scope, Object[] params); - - /** - * Is the client connected to the scope. Result depends on connection type, - * true for persistent and polling connections, false for transient. - * - * @return true if the connection is persistent or polling, - * otherwise false - */ - public boolean isConnected(); - - /** - * Close this connection. This will disconnect the client from the - * associated scope. - */ - public void close(); - - /** - * Return the parameters that were given in the call to "connect". - * - * @return Connection parameters passed from client-side (Flex/Flash application) - */ - public Map getConnectParams(); - - /** - * Sets the Client. - * - * @param client - */ - public void setClient(IClient client); - - /** - * Get the client object associated with this connection. - * - * @return Client object - */ - public IClient getClient(); - - /** - * Get the hostname that the client is connected to. If they are connected - * to an IP, the IP address will be returned as a String. - * - * @return String containing the hostname - */ - public String getHost(); - - /** - * Get the IP address the client is connected from. - * - * @return The IP address of the client - */ - public String getRemoteAddress(); - - /** - * Get the IP addresses the client is connected from. If a client is connected - * through RTMPT and uses a proxy to connect, this will contain all hosts the - * client used to connect to the server. - * - * @return The IP addresses of the client - */ - public List getRemoteAddresses(); - - /** - * Get the port the client is connected from. - * - * @return The port of the client - */ - public int getRemotePort(); - - /** - * Get the path for this connection. - * This is not updated if you switch scope. - * - * @return path Connection path - */ - public String getPath(); - - /** - * Get the session id, this may be null. - * - * @return Session id - */ - public String getSessionId(); - - /** - * Total number of bytes read from the connection. - * - * @return Number of read bytes - */ - public long getReadBytes(); - - /** - * Total number of bytes written to the connection. - * - * @return Number of written bytes - */ - public long getWrittenBytes(); - - /** - * Total number of messages read from the connection. - * - * @return Number of read messages - */ - public long getReadMessages(); - - /** - * Total number of messages written to the connection. - * - * @return Number of written messages - */ - public long getWrittenMessages(); - - /** - * Total number of messages that have been dropped. - * - * @return Number of dropped messages - */ - public long getDroppedMessages(); - - /** - * Total number of messages that are pending to be sent to the connection. - * - * @return Number of pending messages - */ - public long getPendingMessages(); - - /** - * Return number of written bytes the client reports to have received. - * This is the last value of the BytesRead message received from a client. - * - * @see org.red5.server.net.rtmp.event.BytesRead - * @return number of written bytes received by the client - */ - public long getClientBytesRead(); - - /** - * Start measuring the round-trip time for a packet on the connection. - */ - public void ping(); - - /** - * Return round-trip time of last ping command. - * - * @return round-trip time in milliseconds - */ - public int getLastPingTime(); - - /** - * Get the scope this is connected to. - * - * @return The connected scope - */ - public IScope getScope(); - - /** - * Get the basic scopes this connection has subscribed. This list will - * contain the shared objects and broadcast streams the connection - * connected to. - * - * @return List of basic scopes - */ - public Iterator getBasicScopes(); - - /** - * Sets the bandwidth using a mbit/s value. - * - * @param mbits - */ - public void setBandwidth(int mbits); - - /** - * Adds a listener to this object - * - * @param listener - */ - public void addListener(IConnectionListener listener); - - /** - * Removes the listener from this object - * - * @param listener - */ - public void removeListener(IConnectionListener listener); - - /** - * Returns the current stream id. - * - * @return stream id - */ - public int getStreamId(); - - /** - * Sets the current stream id. - * - * @param id stream id - */ - public void setStreamId(int id); - -} diff --git a/src/main/java/org/red5/server/api/IContext.java b/src/main/java/org/red5/server/api/IContext.java deleted file mode 100644 index a376098d4..000000000 --- a/src/main/java/org/red5/server/api/IContext.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api; - -import org.red5.server.api.persistence.IPersistenceStore; -import org.red5.server.api.scope.IGlobalScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeHandler; -import org.red5.server.api.service.IServiceInvoker; -import org.springframework.context.ApplicationContext; -import org.springframework.core.io.support.ResourcePatternResolver; - -/** - * The current context, this object basically wraps the Spring context - * or in the case of the .Net version, any similar system. - * - */ -public interface IContext extends ResourcePatternResolver { - - public static final String ID = "red5.context"; - - /** - * Getter for application context - * - * @return Application context - */ - ApplicationContext getApplicationContext(); - - // public IScopeResolver getScopeResolver(); - /** - * Get client registry. Client registry is a place where all clients are - * registred. - * - * @return Client registry object - */ - IClientRegistry getClientRegistry(); - - /** - * Returns service invoker object. Service invokers are objects that make - * service calls to client side NetConnection objects. - * - * @return Service invoker object - */ - IServiceInvoker getServiceInvoker(); - - /** - * Returns persistence store object, a storage for persistent objects like - * persistent SharedObjects. - * - * @return Persistence store object - */ - IPersistenceStore getPersistanceStore(); - - /** - * Returns scope handler (object that handle all actions related to the - * scope) by path. See {@link IScopeHandler} for details. - * - * @param path - * Path of scope handler - * @return Scope handler - */ - IScopeHandler lookupScopeHandler(String path); - - /** - * Returns scope by path. You can think of IScope as of tree items, used to - * separate context and resources between users. See {@link IScope} for more - * details. - * - * @param path - * Path of scope - * @return IScope object - */ - IScope resolveScope(String path); - - /** - * Returns scope by path from given root. You can think of IScope as of tree - * items, used to separate context and resources between users. - * See {@link IScope} for more details. - * - * @param root - * Root to start from - * @param path - * Path of scope - * @return IScope object - */ - IScope resolveScope(IScope root, String path); - - /** - * Returns global scope reference - * - * @return global scope reference - */ - IGlobalScope getGlobalScope(); - - /** - * Returns service by name. - * - * @param serviceName - * Name of service - * @return Service object - */ - Object lookupService(String serviceName); - - /** - * Returns bean by ID - * - * @param beanId - * Bean ID - * @return Given bean instance - */ - Object getBean(String beanId); - - /** - * Returns true if the context contains a certain bean, - * false otherwise. - * @param beanId The name of the bean to find. - * @return True if the bean exists, false otherwise. - */ - boolean hasBean(String beanId); - - /** - * Returns core service by bean id - * - * @param beanId - * Bean ID - * @return Core service - */ - Object getCoreService(String beanId); - - /** - * Returns IMappingStrategy object - * - * @return IMappingStrategy object - */ - public IMappingStrategy getMappingStrategy(); -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/ICoreObject.java b/src/main/java/org/red5/server/api/ICoreObject.java deleted file mode 100644 index 170656d1e..000000000 --- a/src/main/java/org/red5/server/api/ICoreObject.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api; - -import org.red5.server.api.event.IEventDispatcher; -import org.red5.server.api.event.IEventHandler; -import org.red5.server.api.event.IEventListener; - -/** - * Base marker interface for all core objects. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - * - */ -public interface ICoreObject extends IEventDispatcher, IEventHandler, IEventListener { - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/IMappingStrategy.java b/src/main/java/org/red5/server/api/IMappingStrategy.java deleted file mode 100644 index ab8d66484..000000000 --- a/src/main/java/org/red5/server/api/IMappingStrategy.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api; - -/** - * This interface encapsulates the mapping strategy used by the context. - */ -public interface IMappingStrategy { - - /** - * Map a name to the name of a service. - * - * @param name name to map - * @return The name of the service with the passed name - */ - public String mapServiceName(String name); - - /** - * Map a context path to the name of a scope handler. - * - * @param contextPath context path to map - * @return The name of a scope handler - */ - public String mapScopeHandlerName(String contextPath); - - /** - * Map a context path to a path prefix for resources. - * - * @param contextPath context path to map - * @return The path prefix for resources with the given name - */ - public String mapResourcePrefix(String contextPath); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/IServer.java b/src/main/java/org/red5/server/api/IServer.java deleted file mode 100644 index 4b8336906..000000000 --- a/src/main/java/org/red5/server/api/IServer.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api; - -import java.util.Iterator; -import java.util.Map; - -import org.red5.server.api.listeners.IConnectionListener; -import org.red5.server.api.listeners.IScopeListener; -import org.red5.server.api.scope.IGlobalScope; - -/** - * The interface that represents the Red5 server. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - * - */ -public interface IServer { - /** - * Server ID - */ - public static final String ID = "red5.server"; - - /** - * Get the global scope with given name. - * - * @param name Name of the global scope - * @return the global scope - */ - public IGlobalScope getGlobal(String name); - - /** - * Register a global scope. - * - * @param scope The global scope to register - */ - public void registerGlobal(IGlobalScope scope); - - /** - * Lookup the global scope for a host. - * - * @param hostName The name of the host - * @param contextPath The path in the host - * @return The found global scope or null - */ - public IGlobalScope lookupGlobal(String hostName, String contextPath); - - /** - * Map a virtual hostname and a path to the name of a global scope. - * - * @param hostName The name of the host to map - * @param contextPath The path to map - * @param globalName The name of the global scope to map to - * @return true if the name was mapped, otherwise - * false - */ - public boolean addMapping(String hostName, String contextPath, String globalName); - - /** - * Unregister a previously mapped global scope. - * - * @param hostName The name of the host to unmap - * @param contextPath The path for this host to unmap - * @return true if the global scope was unmapped, otherwise - * false - */ - public boolean removeMapping(String hostName, String contextPath); - - /** - * Query informations about the global scope mappings. - * - * @return Map containing informations about the mappings - */ - public Map getMappingTable(); - - /** - * Get list of global scope names. - * - * @return Iterator for names of global scopes - */ - public Iterator getGlobalNames(); - - /** - * Get list of global scopes. - * - * @return Iterator for global scopes objects - */ - public Iterator getGlobalScopes(); - - /** - * Add listener to get notified about scope events. - * - * @param listener the listener to add - */ - public void addListener(IScopeListener listener); - - /** - * Add listener to get notified about connection events. - * - * @param listener the listener to add - */ - public void addListener(IConnectionListener listener); - - /** - * Remove listener that got notified about scope events. - * - * @param listener the listener to remove - */ - public void removeListener(IScopeListener listener); - - /** - * Remove listener that got notified about connection events. - * - * @param listener the listener to remove - */ - public void removeListener(IConnectionListener listener); - -} diff --git a/src/main/java/org/red5/server/api/Red5.java b/src/main/java/org/red5/server/api/Red5.java deleted file mode 100644 index 09ebda671..000000000 --- a/src/main/java/org/red5/server/api/Red5.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2012 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.api; - -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.Map; - -import javax.management.openmbean.CompositeData; - -import org.red5.logging.Red5LoggerFactory; -import org.red5.server.api.scope.IScope; -import org.slf4j.Logger; - -/** - * Utility class for accessing Red5 API objects. - * - * This class uses a thread local, and will be setup by the service invoker. - * - * The code below shows various uses. - *
- *
 
- * IConnection conn = Red5.getConnectionLocal();
- * Red5 r5 = new Red5(); 
- * IScope scope = r5.getScope();
- * conn = r5.getConnection();
- * r5 = new Red5(conn);
- * IClient client = r5.getClient();
- * 
- * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - * @author Paul Gregoire (mondain@gmail.com) - * @author Tiago Daniel Jacobs (os@tdj.cc) - */ -public final class Red5 { - - private static Logger log = Red5LoggerFactory.getLogger(Red5.class); - - /** - * Connection associated with the current thread. Each connection runs in a separate thread. - */ - private static final ThreadLocal> connThreadLocal = new ThreadLocal>(); - - /** - * Connection local to the current thread - */ - public IConnection conn; - - /** - * Server version with revision - */ - public static final String VERSION = "Red5 Server 1.0.4-SNAPSHOT"; - - /** - * Server version for fmsVer requests - */ - public static final String FMS_VERSION = "RED5/1,0,4,0"; - - /** - * Server capabilities - */ - public static final Integer CAPABILITIES = Integer.valueOf(33); // was 31 - - /** - * Data version for NetStatusEvents - */ - @SuppressWarnings("serial") - public static final Map DATA_VERSION = new HashMap(2) { - { - put("version", "4,0,0,1121"); - put("type", "red5"); - } - }; - - /** - * Server start time - */ - private static final long START_TIME = System.currentTimeMillis(); - - /** - * Detection of debug mode - */ - private static boolean debug = java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("jdwp") >= 0; - - /** - * Create a new Red5 object using given connection. - * - * @param conn Connection object. - */ - public Red5(IConnection conn) { - this.conn = conn; - } - - /** - * Create a new Red5 object using the connection local to the current thread - * A bit of magic that lets you access the red5 scope from anywhere - */ - public Red5() { - conn = Red5.getConnectionLocal(); - } - - /** - * Setter for connection - * - * @param connection Thread local connection - */ - public static void setConnectionLocal(IConnection connection) { - log.debug("Set connection: {} with thread: {}", (connection != null ? connection.getSessionId() : null), Thread.currentThread().getName()); - if (connection != null) { - connThreadLocal.set(new WeakReference(connection)); - IScope scope = connection.getScope(); - if (scope != null) { - Thread.currentThread().setContextClassLoader(scope.getClassLoader()); - } - } else { - // use null to clear the value - connThreadLocal.remove(); - } - } - - /** - * Get the connection associated with the current thread. This method allows - * you to get connection object local to current thread. When you need to - * get a connection associated with event handler and so forth, this method - * provides you with it. - * - * @return Connection object - */ - public static IConnection getConnectionLocal() { - log.debug("Get connection on thread: {}", Thread.currentThread().getName()); - WeakReference ref = connThreadLocal.get(); - if (ref != null) { - return ref.get(); - } else { - return null; - } - } - - /** - * Get the connection object. - * - * @return Connection object - */ - public IConnection getConnection() { - return conn; - } - - /** - * Get the scope - * - * @return Scope object - */ - public IScope getScope() { - return conn.getScope(); - } - - /** - * Get the client - * - * @return Client object - */ - public IClient getClient() { - return conn.getClient(); - } - - /** - * Get the spring application context - * - * @return Application context - */ - public IContext getContext() { - return conn.getScope().getContext(); - } - - /** - * Returns the current version with revision number - * - * @return String version - */ - public static String getVersion() { - return VERSION; - } - - /** - * Returns the current version for fmsVer requests - * - * @return String fms version - */ - public static String getFMSVersion() { - return FMS_VERSION; - } - - public static Integer getCapabilities() { - return CAPABILITIES; - } - - public static Object getDataVersion() { - return DATA_VERSION; - } - - /** - * Returns true if java debugging was detected. - * - * @return true if debugging, false otherwise - */ - public static boolean isDebug() { - return debug; - } - - /** - * Returns server uptime in milliseconds. - * - * @return String version - */ - public static long getUpTime() { - return System.currentTimeMillis() - START_TIME; - } - - /** - * Allows for reconstruction via CompositeData. - * - * @param cd composite data - * @return Red5 class instance - */ - public static Red5 from(CompositeData cd) { - Red5 instance = null; - if (cd.containsKey("connection")) { - Object cn = cd.get("connection"); - if (cn != null && cn instanceof IConnection) { - instance = new Red5((IConnection) cn); - } else { - instance = new Red5(); - } - } else { - instance = new Red5(); - } - return instance; - } - -} diff --git a/src/main/java/org/red5/server/api/event/IEvent.java b/src/main/java/org/red5/server/api/event/IEvent.java deleted file mode 100644 index c8c377e66..000000000 --- a/src/main/java/org/red5/server/api/event/IEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.event; - -/** - * IEvent interfaces is the essential interface every Event should implement - */ -public interface IEvent { - - /** - * Returns even type - * - * @return Event type enumeration - */ - public Type getType(); - - /** - * Returns event context object - * - * @return Event context object - */ - public Object getObject(); - - /** - * Whether event has source (event listener(s)) - * @return true if so, false otherwise - */ - public boolean hasSource(); - - /** - * Returns event listener - * @return Event listener object - */ - public IEventListener getSource(); - - enum Type { - SYSTEM, STATUS, SERVICE_CALL, SHARED_OBJECT, STREAM_ACTION, STREAM_CONTROL, STREAM_DATA, CLIENT, CLIENT_INVOKE, CLIENT_NOTIFY, SERVER - } - -} diff --git a/src/main/java/org/red5/server/api/event/IEventDispatcher.java b/src/main/java/org/red5/server/api/event/IEventDispatcher.java deleted file mode 100644 index d49cac1e7..000000000 --- a/src/main/java/org/red5/server/api/event/IEventDispatcher.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.event; - -/** - * IEventDispatcher interface implementations dispatch events - */ -public interface IEventDispatcher { - - /** - * Dispatches event - * @param event Event object - */ - public void dispatchEvent(IEvent event); - -} diff --git a/src/main/java/org/red5/server/api/event/IEventHandler.java b/src/main/java/org/red5/server/api/event/IEventHandler.java deleted file mode 100644 index 0220fce0f..000000000 --- a/src/main/java/org/red5/server/api/event/IEventHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.event; - -public interface IEventHandler { - - /** - * Handle an event. - * - * @param event to handle - * @return true if event was handled, false if it should bubble - */ - public boolean handleEvent(IEvent event); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/event/IEventListener.java b/src/main/java/org/red5/server/api/event/IEventListener.java deleted file mode 100644 index 32581622c..000000000 --- a/src/main/java/org/red5/server/api/event/IEventListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.event; - -public interface IEventListener { - - /** - * Notify of event. - * @param event the event object - */ - public void notifyEvent(IEvent event); - -} diff --git a/src/main/java/org/red5/server/api/event/IEventObservable.java b/src/main/java/org/red5/server/api/event/IEventObservable.java deleted file mode 100644 index 9ff63c114..000000000 --- a/src/main/java/org/red5/server/api/event/IEventObservable.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.event; - -import java.util.Set; - -/** - * IEventObservable hold functionality of the well-known Observer pattern, that is - * it has a list of objects that listen to events. - */ -public interface IEventObservable { - - /** - * Add event listener to this observable - * - * @param listener Event listener - * @return true if listener is added and false otherwise - */ - public boolean addEventListener(IEventListener listener); - - /** - * Remove event listener from this observable - * - * @param listener Event listener - * @return true if listener is removed and false otherwise - */ - public boolean removeEventListener(IEventListener listener); - - /** - * Returns event listeners - * - * @return Event listeners iterator - */ - public Set getEventListeners(); - -} diff --git a/src/main/java/org/red5/server/api/listeners/IConnectionListener.java b/src/main/java/org/red5/server/api/listeners/IConnectionListener.java deleted file mode 100644 index de2f21808..000000000 --- a/src/main/java/org/red5/server/api/listeners/IConnectionListener.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.listeners; - -import org.red5.server.api.IConnection; - -/** - * Interface for listeners to connection events. - * - * @author The Red5 Project - * @author Joachim Bauch (bauch@struktur.de) - */ -public interface IConnectionListener { - - /** - * A new connection was established. - * - * @param conn the new connection - */ - public void notifyConnected(IConnection conn); - - /** - * A connection was disconnected. - * - * @param conn the disconnected connection - */ - public void notifyDisconnected(IConnection conn); - -} diff --git a/src/main/java/org/red5/server/api/listeners/IScopeListener.java b/src/main/java/org/red5/server/api/listeners/IScopeListener.java deleted file mode 100644 index cc90bb6a0..000000000 --- a/src/main/java/org/red5/server/api/listeners/IScopeListener.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.listeners; - -import org.red5.server.api.scope.IScope; - -/** - * Interface for listeners to scope events. - * - * @author The Red5 Project - * @author Joachim Bauch (bauch@struktur.de) - */ -public interface IScopeListener { - - /** - * A scope has been created. - * - * @param scope the new scope - */ - public void notifyScopeCreated(IScope scope); - - /** - * A scope has been removed. - * - * @param scope the removed scope - */ - public void notifyScopeRemoved(IScope scope); - -} diff --git a/src/main/java/org/red5/server/api/persistence/IPersistable.java b/src/main/java/org/red5/server/api/persistence/IPersistable.java deleted file mode 100644 index 14b3f2dbc..000000000 --- a/src/main/java/org/red5/server/api/persistence/IPersistable.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.persistence; - -import java.io.IOException; - -import org.red5.io.object.Input; -import org.red5.io.object.Output; - -/** - * Base interface for objects that can be made persistent. - * - * Every object that complies to this interface must provide either a - * constructor that takes an input stream as only parameter or an empty - * constructor so it can be loaded from the persistence store. - * - * However this is not required for objects that are created by the application - * and initialized afterwards. - * - * @see org.red5.io.object.Input - * @see IPersistenceStore#load(String) - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - * @author Joachim Bauch (jojo@struktur.de) - */ - -public interface IPersistable { - - /** - * Prefix for attribute names that should not be made persistent. - */ - public static final String TRANSIENT_PREFIX = "_transient"; - - /** - * Returns true if the object is persistent, - * false otherwise. - * - * @return true if object is persistent, false otherwise - */ - public boolean isPersistent(); - - /** - * Set the persistent flag of the object. - * - * @param persistent true if object is persistent, false otherwise - */ - public void setPersistent(boolean persistent); - - /** - * Returns the name of the persistent object. - * - * @return Object name - */ - public String getName(); - - /** - * Set the name of the persistent object. - * - * @param name New object name - */ - public void setName(String name); - - /** - * Returns the type of the persistent object. - * - * @return Object type - */ - public String getType(); - - /** - * Returns the path of the persistent object. - * - * @return Persisted object path - */ - public String getPath(); - - /** - * Set the path of the persistent object. - * - * @param path New persisted object path - */ - public void setPath(String path); - - /** - * Returns the timestamp when the object was last modified. - * - * @return Last modification date in milliseconds - */ - public long getLastModified(); - - /** - * Returns the persistence store this object is stored in - * - * @return This object's persistence store - */ - public IPersistenceStore getStore(); - - /** - * Store a reference to the persistence store in the object. - * - * @param store - * Store the object is saved in - */ - void setStore(IPersistenceStore store); - - /** - * Write the object to the passed output stream. - * - * @param output - * Output stream to write to - * @throws java.io.IOException Any I/O exception - */ - void serialize(Output output) throws IOException; - - /** - * Load the object from the passed input stream. - * - * @param input - * Input stream to load from - * @throws java.io.IOException Any I/O exception - */ - void deserialize(Input input) throws IOException; - -} diff --git a/src/main/java/org/red5/server/api/persistence/IPersistenceStore.java b/src/main/java/org/red5/server/api/persistence/IPersistenceStore.java deleted file mode 100644 index eeae2a469..000000000 --- a/src/main/java/org/red5/server/api/persistence/IPersistenceStore.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.persistence; - -import java.util.Collection; -import java.util.Set; - -/** - * Storage for persistent objects. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - * @author Joachim Bauch (jojo@struktur.de) - */ - -public interface IPersistenceStore { - - /** - * Persist given object. - * - * @param obj Object to store - * @return true on success, false otherwise - */ - public boolean save(IPersistable obj); - - /** - * Load a persistent object with the given name. The object must provide - * either a constructor that takes an input stream as only parameter or an - * empty constructor so it can be loaded from the persistence store. - * - * @param name the name of the object to load - * @return The loaded object or null if no such object was - * found - */ - public IPersistable load(String name); - - /** - * Load state of an already instantiated persistent object. - * - * @param obj the object to initializ - * @return true if the object was initialized, false otherwise - */ - public boolean load(IPersistable obj); - - /** - * Delete the passed persistent object. - * - * @param obj the object to delete - * @return true if object was persisted and thus can be removed, false otherwise - */ - public boolean remove(IPersistable obj); - - /** - * Delete the persistent object with the given name. - * - * @param name the name of the object to delete - * @return true if object was persisted and thus can be removed, false otherwise - */ - public boolean remove(String name); - - /** - * Return iterator over the names of all already loaded objects in the - * storage. - * - * @return Set of all object names - */ - public Set getObjectNames(); - - /** - * Return iterator over the already loaded objects in the storage. - * - * @return Set of all objects - */ - public Collection getObjects(); - - /** - * Notify store that it's being closed. This allows the store to write - * any pending objects to disk. - */ - public void notifyClose(); - -} diff --git a/src/main/java/org/red5/server/api/persistence/PersistenceUtils.java b/src/main/java/org/red5/server/api/persistence/PersistenceUtils.java deleted file mode 100644 index 0747faf28..000000000 --- a/src/main/java/org/red5/server/api/persistence/PersistenceUtils.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.persistence; - -import java.lang.reflect.Constructor; - -import org.springframework.core.io.support.ResourcePatternResolver; - -/** - * Helper class for persistence. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public class PersistenceUtils { - - /** - * Returns persistence store object class constructor - * - * @param theClass Persistence store class - * @param interfaces Interfaces that are being implemented by persistence store object class - * @return Constructor - * @throws Exception - */ - private static Constructor getPersistenceStoreConstructor(Class theClass, Class[] interfaces) throws Exception { - Constructor constructor = null; - for (Class interfaceClass : interfaces) { - try { - constructor = theClass.getConstructor(new Class[] { interfaceClass }); - } catch (NoSuchMethodException err) { - // Ignore this error - } - if (constructor != null) { - break; - } - constructor = getPersistenceStoreConstructor(theClass, interfaceClass.getInterfaces()); - if (constructor != null) { - break; - } - } - return constructor; - } - - /** - * Returns persistence store object. Persistence store is a special object - * that stores persistence objects and provides methods to manipulate them - * (save, load, remove, list). - * - * @param resolver Resolves connection pattern into Resource object - * @param className Name of persistence class - * @return IPersistence store object that provides methods for persistence object handling - * @throws Exception if error - */ - public static IPersistenceStore getPersistenceStore(ResourcePatternResolver resolver, String className) throws Exception { - Class persistenceClass = Class.forName(className); - Constructor constructor = getPersistenceStoreConstructor(persistenceClass, resolver.getClass().getInterfaces()); - if (constructor == null) { - // Search in superclasses of the object. - Class superClass = resolver.getClass().getSuperclass(); - while (superClass != null) { - constructor = getPersistenceStoreConstructor(persistenceClass, superClass.getInterfaces()); - if (constructor != null) { - break; - } - superClass = superClass.getSuperclass(); - } - } - if (constructor == null) { - throw new NoSuchMethodException(); - } - return (IPersistenceStore) constructor.newInstance(new Object[] { resolver }); - } - -} diff --git a/src/main/java/org/red5/server/api/remoting/IRemotingHeader.java b/src/main/java/org/red5/server/api/remoting/IRemotingHeader.java deleted file mode 100644 index 5b8174c60..000000000 --- a/src/main/java/org/red5/server/api/remoting/IRemotingHeader.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.remoting; - -/** - * A Remoting header. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IRemotingHeader { - - /** Name of header specifying string to add to gateway url. */ - public static final String APPEND_TO_GATEWAY_URL = "AppendToGatewayUrl"; - - /** Name of header specifying new gateway url to use. */ - public static final String REPLACE_GATEWAY_URL = "ReplaceGatewayUrl"; - - /** Name of header specifying new header to send. */ - public static final String PERSISTENT_HEADER = "RequestPersistentHeader"; - - /** Name of header containing authentication data. */ - public static final String CREDENTIALS = "Credentials"; - - /** Name of header to request debug informations from the server. */ - public static final String DEBUG_SERVER = "amf_server_debug"; - - /** - * Return name of header. - * - * @return name of header - */ - public String getName(); - - /** - * Return value of header. - * - * @return value of header - */ - public Object getValue(); - - /** - * Return boolean flag if receiver must process header before handling - * other headers or messages. - * - * @return must understand - */ - public boolean getMustUnderstand(); - -} diff --git a/src/main/java/org/red5/server/api/scheduling/IScheduledJob.java b/src/main/java/org/red5/server/api/scheduling/IScheduledJob.java deleted file mode 100644 index 739d19bce..000000000 --- a/src/main/java/org/red5/server/api/scheduling/IScheduledJob.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.scheduling; - -/** - * Interface that must be implemented by classes that can be scheduled for - * periodic execution. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IScheduledJob { - - /** - * Called each time the job is triggered by the scheduling service. - * - * @param service the service that called the job - * @throws CloneNotSupportedException throws if Darth Vader attempts to use - * this object for his own nefarious purposes. - */ - public void execute(ISchedulingService service) throws CloneNotSupportedException; - -} diff --git a/src/main/java/org/red5/server/api/scheduling/ISchedulingService.java b/src/main/java/org/red5/server/api/scheduling/ISchedulingService.java deleted file mode 100644 index 417defcfb..000000000 --- a/src/main/java/org/red5/server/api/scheduling/ISchedulingService.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.scheduling; - -import java.util.Date; -import java.util.List; - -import org.red5.server.api.scope.IScopeService; - -/** - * Service that supports periodic execution of jobs, adding, removing and - * getting their name as list. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * @author Paul Gregoire (mondain@gmail.com) - */ -public interface ISchedulingService extends IScopeService { - - public static String BEAN_NAME = "schedulingService"; - - /** - * Schedule a job for periodic execution. - * - * @param interval time in milliseconds between two notifications of the job - * @param job the job to trigger periodically - * @return the name of the scheduled job - */ - public String addScheduledJob(int interval, IScheduledJob job); - - /** - * Schedule a job for single execution in the future. Please note - * that the jobs are not saved if Red5 is restarted in the meantime. - * - * @param timeDelta time delta in milliseconds from the current date - * @param job the job to trigger - * @return the name of the scheduled job - */ - public String addScheduledOnceJob(long timeDelta, IScheduledJob job); - - /** - * Schedule a job for single execution at a given date. Please note - * that the jobs are not saved if Red5 is restarted in the meantime. - * - * @param date date when the job should be executed - * @param job the job to trigger - * @return the name of the scheduled job - */ - public String addScheduledOnceJob(Date date, IScheduledJob job); - - /** - * Schedule a job for periodic execution which will start after the specifed delay. - * - * @param interval time in milliseconds between two notifications of the job - * @param job the job to trigger periodically - * @param delay time in milliseconds to pass before first execution. - * @return - * the name of the scheduled job - */ - public String addScheduledJobAfterDelay(int interval, IScheduledJob job, int delay); - - /** - * Pauses the trigger which initiates job execution. - * - * @param name name of the job to stop - */ - public void pauseScheduledJob(String name); - - /** - * Resumes the trigger which initiates job execution. - * - * @param name name of the job to stop - */ - public void resumeScheduledJob(String name); - - /** - * Stop executing a previously scheduled job. - * - * @param name name of the job to stop - */ - public void removeScheduledJob(String name); - - /** - * Return names of scheduled jobs. - * - * @return list of job names - */ - public List getScheduledJobNames(); - -} diff --git a/src/main/java/org/red5/server/api/scope/IBasicScope.java b/src/main/java/org/red5/server/api/scope/IBasicScope.java deleted file mode 100644 index dc223c2d5..000000000 --- a/src/main/java/org/red5/server/api/scope/IBasicScope.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.scope; - -import org.red5.server.api.ICoreObject; -import org.red5.server.api.event.IEventObservable; -import org.red5.server.api.persistence.IPersistenceStore; - -/** - * Base interface for all scope objects, including SharedObjects. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - */ -public interface IBasicScope extends ICoreObject, IEventObservable { - - /** - * Does this scope have a parent? You can think of scopes as of tree items - * where scope may have a parent and children (child). - * - * @return true if this scope has a parent, otherwise - * false - */ - public boolean hasParent(); - - /** - * Get this scopes parent. - * - * @return parent scope, or null if this scope doesn't have a - * parent - */ - public IScope getParent(); - - /** - * Get the scopes depth, how far down the scope tree is it. The lowest depth - * is 0x00, the depth of Global scope. Application scope depth is 0x01. Room - * depth is 0x02, 0x03 and so forth. - * - * @return the depth - */ - public int getDepth(); - - /** - * Get the name of this scope. Eg. someroom. - * - * @return the name - */ - public String getName(); - - /** - * Get the persistable store - * @return the store - */ - public IPersistenceStore getStore(); - - /** - * Get the full absolute path. Eg. host/myapp/someroom. - * - * @return Absolute scope path - */ - public String getPath(); - - /** - * Get the type of the scope. - * - * @return Type of scope - */ - public ScopeType getType(); - - /** - * Sets the amount of time to keep the scope available after the - * last disconnect. - * - * @param keepDelay delay - */ - public void setKeepDelay(int keepDelay); - - /** - * Validates a scope based on its name and type - * - * @return true if both name and type are valid, false otherwise - */ - public boolean isValid(); - -} diff --git a/src/main/java/org/red5/server/api/scope/IBroadcastScope.java b/src/main/java/org/red5/server/api/scope/IBroadcastScope.java deleted file mode 100644 index 1f8d0e81a..000000000 --- a/src/main/java/org/red5/server/api/scope/IBroadcastScope.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.scope; - -import org.red5.server.api.stream.IClientBroadcastStream; -import org.red5.server.messaging.IPipe; - -/** - * Broadcast scope is marker interface that represents object that works as basic scope and - * has pipe connection event dispatching capabilities. - */ -public interface IBroadcastScope extends IBasicScope, IPipe { - - public IClientBroadcastStream getClientBroadcastStream(); - - public void setClientBroadcastStream(IClientBroadcastStream clientBroadcastStream); - -} diff --git a/src/main/java/org/red5/server/api/scope/IGlobalScope.java b/src/main/java/org/red5/server/api/scope/IGlobalScope.java deleted file mode 100644 index 7afcc6384..000000000 --- a/src/main/java/org/red5/server/api/scope/IGlobalScope.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.scope; - -import org.red5.server.api.IServer; - -/** - * The global scope that acts as root for all applications in a host. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - * - */ -public interface IGlobalScope extends IScope { - - /** - * Register the global scope in the server and initialize it - */ - public void register(); - - /** - * Return the server this global scope runs in. - * - * @return the server - */ - public IServer getServer(); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/scope/IScope.java b/src/main/java/org/red5/server/api/scope/IScope.java deleted file mode 100644 index c190df52c..000000000 --- a/src/main/java/org/red5/server/api/scope/IScope.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.scope; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -import org.red5.server.api.IClient; -import org.red5.server.api.IConnection; -import org.red5.server.api.IContext; -import org.red5.server.api.service.IServiceHandlerProvider; -import org.red5.server.api.statistics.IScopeStatistics; -import org.springframework.core.io.support.ResourcePatternResolver; - -/** - * The scope object. - * - * A stateful object shared between a group of clients connected to the same - * context path. Scopes are arranged in hierarchical way, so its possible for - * a scope to have a parent and children scopes. If a client connects to a scope then they are - * also connected to its parent scope. The scope object is used to access - * resources, shared object, streams, etc. That is, scope are general option for grouping things - * in application. - * - * The following are all names for scopes: application, room, place, lobby. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - */ -public interface IScope extends IBasicScope, ResourcePatternResolver, IServiceHandlerProvider { - - /** - * Scope separator - */ - public static final String SEPARATOR = ":"; - - /** - * Check to see if this scope has a child scope matching a given name. - * - * @param name the name of the child scope - * @return true if a child scope exists, otherwise - * false - */ - public boolean hasChildScope(String name); - - /** - * Checks whether scope has a child scope with given name and type - * - * @param type Child scope type - * @param name Child scope name - * @return true if a child scope exists, otherwise - * false - */ - public boolean hasChildScope(ScopeType type, String name); - - /** - * Creates child scope with name given and returns success value. Returns - * true on success, false if given scope - * already exists among children. - * - * @param name New child scope name - * @return true if child scope was successfully creates, - * false otherwise - */ - public boolean createChildScope(String name); - - /** - * Adds scope as a child scope. Returns true on success, - * false if given scope is already a child of current. - * - * @param scope Scope given - * @return true if child scope was successfully added, - * false otherwise - */ - public boolean addChildScope(IBasicScope scope); - - /** - * Removes scope from the children scope list. - * - * @param scope Scope given - */ - public void removeChildScope(IBasicScope scope); - - /** - * Removes all the child scopes - */ - public void removeChildren(); - - /** - * Get a set of the child scope names. - * - * @return set containing child scope names - */ - public Set getScopeNames(); - - public Set getBasicScopeNames(ScopeType type); - - /** - * Return the broadcast scope for a given name - * - * @param name - * @return broadcast scope or null if not found - */ - public IBroadcastScope getBroadcastScope(String name); - - /** - * Get a child scope by type and name. - * - * @param type Child scope type - * @param name Name of the child scope - * @return the child scope, or null if no scope is found - */ - public IBasicScope getBasicScope(ScopeType type, String name); - - /** - * Return scope by name - * - * @param name Scope name - * @return Scope with given name - */ - public IScope getScope(String name); - - /** - * Get a set of connected clients. You can get the connections by passing - * the scope to the clients {@link IClient#getConnections()} method. - * - * @return Set containing all connected clients - * @see org.red5.server.api.IClient#getConnections(IScope) - */ - public Set getClients(); - - /** - * Get a connection iterator. You can call remove, and the connection will - * be closed. - * - * @deprecated Use {@link IScope#getClientConnections()} instead - * @return Iterator holding all connections - */ - @Deprecated - public Collection> getConnections(); - - /** - * Get all current connections. You can call remove, and the connection will - * be closed. - * - * @return Set containing all connections - */ - public Set getClientConnections(); - - /** - * Lookup connections. - * - * @deprecated Use {@link IScope#lookupConnection(IClient)} instead - * @param client object - * @return Set of connection objects (read-only) - */ - @Deprecated - public Set lookupConnections(IClient client); - - /** - * Lookup connection for a given client. - * - * @param client object - * @return connection object - */ - public IConnection lookupConnection(IClient client); - - /** - * Returns scope context - * - * @return Scope context - */ - public IContext getContext(); - - /** - * Checks whether scope has handler or not. - * - * @return true if scope has a handler, false - * otherwise - */ - public boolean hasHandler(); - - /** - * Return handler of the scope - * - * @return Scope handler - */ - public IScopeHandler getHandler(); - - /** - * Return context path. - * - * @return Context path - */ - public String getContextPath(); - - /** - * Adds given connection to the scope - * - * @param conn Given connection - * @return true on success, false if given - * connection already belongs to this scope - */ - public boolean connect(IConnection conn); - - /** - * Add given connection to the scope, overloaded for parameters pass case. - * @param conn Given connection - * @param params Parameters passed - * @return true on success, false if given - * connection already belongs to this scope - */ - public boolean connect(IConnection conn, Object[] params); - - /** - * Removes given connection from list of scope connections. This disconnects - * all clients of given connection from the scope. - * - * @param conn Connection given - */ - public void disconnect(IConnection conn); - - /** - * Return statistics informations about the scope. - * - * @return statistics - */ - public IScopeStatistics getStatistics(); - - /** - * Set attribute by name - * - * @param name - * @param value - * @return true if added, false if not added - */ - public boolean setAttribute(String name, Object value); - - /** - * Get attribute by name - * - * @param name - * @return value for the given name in the attributes or null if not found - */ - public Object getAttribute(String name); - - /** - * Whether or not an attribute exists, keyed by the given name - * - * @param name - * @return true if it exists, false otherwise - */ - public boolean hasAttribute(String name); - - /** - * Remove attribute by name - * - * @param name - * @return true if removed, false otherwise - */ - public boolean removeAttribute(String name); - - /** - * Return attribute names - * - * @return attribute names - */ - public Set getAttributeNames(); - - /** - * Return scope attributes - * - * @return attributes - */ - public Map getAttributes(); - -} diff --git a/src/main/java/org/red5/server/api/scope/IScopeAware.java b/src/main/java/org/red5/server/api/scope/IScopeAware.java deleted file mode 100644 index 28ad0187d..000000000 --- a/src/main/java/org/red5/server/api/scope/IScopeAware.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.scope; - -/** - * Maker interface for all objects that are aware of the scope they are located - * in. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - */ -public interface IScopeAware { - - /** - * Set the scope the object is located in. - * - * @param scope Scope for this object - */ - public void setScope(IScope scope); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/scope/IScopeHandler.java b/src/main/java/org/red5/server/api/scope/IScopeHandler.java deleted file mode 100644 index 0126adfa2..000000000 --- a/src/main/java/org/red5/server/api/scope/IScopeHandler.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.scope; - -import org.red5.server.api.IClient; -import org.red5.server.api.IConnection; -import org.red5.server.api.event.IEventHandler; -import org.red5.server.api.service.IServiceCall; - -/** - * The scope handler controls actions performed against a scope object, and also - * is notified of all events. - * - * Gives fine grained control over what actions can be performed with the can* - * methods. Allows for detailed reporting on what is happening within the scope - * with the on* methods. This is the core interface users implement to create - * applications. - * - * The thread local connection is always available via the Red5 object within - * these methods - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - */ -public interface IScopeHandler extends IEventHandler { - - /** - * Called when a scope is created for the first time. - * - * @param scope - * the new scope object - * @return true to allow, false to deny - */ - boolean start(IScope scope); - - /** - * Called just before a scope is disposed. - * @param scope Scope that id disposed - */ - void stop(IScope scope); - - /** - * Called just before every connection to a scope. You can pass additional - * params from client using NetConnection.connect method (see - * below). - * - * @param conn - * Connection object - * @param params - * List of params passed from client via - * NetConnection.connect method. All parameters - * but the first one passed to NetConnection.connect - * method are available as params array. - * - * - * @return true to allow, false to deny - * @param scope Scope object - */ - boolean connect(IConnection conn, IScope scope, Object[] params); - - /** - * Called just after the a connection is disconnected. - * - * @param conn - * Connection object - * @param scope - * Scope object - */ - void disconnect(IConnection conn, IScope scope); - - /** - * Called just before a child scope is added. - * - * @param scope - * Scope that will be added - * @return true to allow, false to deny - */ - boolean addChildScope(IBasicScope scope); - - /** - * Called just after a child scope has been removed. - * - * @param scope - * Scope that has been removed - */ - void removeChildScope(IBasicScope scope); - - /** - * Called just before a client enters the scope. - * - * @param client - * Client object - * @return true to allow, false to deny - * connection - * @param scope Scope that is joined by client - */ - boolean join(IClient client, IScope scope); - - /** - * Called just after the client leaves the scope. - * - * @param client - * Client object - * @param scope - * Scope object - */ - void leave(IClient client, IScope scope); - - /** - * Called when a service is called. - * - * @param conn - * The connection object - * @param call - * The call object. - * - * @return true to allow, false to deny - */ - boolean serviceCall(IConnection conn, IServiceCall call); - -} diff --git a/src/main/java/org/red5/server/api/scope/IScopeService.java b/src/main/java/org/red5/server/api/scope/IScopeService.java deleted file mode 100644 index 59d1d2bb6..000000000 --- a/src/main/java/org/red5/server/api/scope/IScopeService.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.scope; - -/** - * Base marker interface for all scope services. Used by the ScopeUtils to lookup - * services defined as beans in Spring application context. A scope service usually can perform various - * tasks on a scope like managing shared objects, streams, etc. - * - * @author The Red5 Project - * @author Joachim Bauch (bauch@struktur.de) - */ -public interface IScopeService { - - /** - * Name of a bean defining that scope service. Override in - * subinterfaces. - * */ - public static String BEAN_NAME = null; - -} diff --git a/src/main/java/org/red5/server/api/scope/ScopeType.java b/src/main/java/org/red5/server/api/scope/ScopeType.java deleted file mode 100644 index 4d8aa85d6..000000000 --- a/src/main/java/org/red5/server/api/scope/ScopeType.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.red5.server.api.scope; - -/** - * Represents all the supported scope types. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public enum ScopeType { - - UNDEFINED, GLOBAL, APPLICATION, ROOM, BROADCAST, SHARED_OBJECT; - -} diff --git a/src/main/java/org/red5/server/api/service/IPendingServiceCall.java b/src/main/java/org/red5/server/api/service/IPendingServiceCall.java deleted file mode 100644 index b5aee9745..000000000 --- a/src/main/java/org/red5/server/api/service/IPendingServiceCall.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.service; - -import java.util.Set; - -/** - * IPendingServiceCall is a call that have a list of callbacks. - * - * - */ -public interface IPendingServiceCall extends IServiceCall { - - /** - * Returns service call result - * - * @return Remote call result - */ - public abstract Object getResult(); - - /** - * Setter for property 'result'. - * - * @param result Value to set for property 'result'. - */ - public abstract void setResult(Object result); - - /** - * Registers callback object usually represented as an anonymous class - * instance that implements IPendingServiceCallback interface. - * - * @param callback Callback object - */ - public void registerCallback(IPendingServiceCallback callback); - - /** - * Unregisters callback object usually represented as an anonymous class - * instance that implements IPendingServiceCallback interface. - * - * @param callback Callback object - */ - public void unregisterCallback(IPendingServiceCallback callback); - - /** - * Returns list of callback objects, usually callback object represented as - * an anonymous class instance that implements IPendingServiceCallback - * interface. - * - * @return Set of pending operations callbacks - */ - public Set getCallbacks(); -} diff --git a/src/main/java/org/red5/server/api/service/IPendingServiceCallback.java b/src/main/java/org/red5/server/api/service/IPendingServiceCallback.java deleted file mode 100644 index 24ff2e5b9..000000000 --- a/src/main/java/org/red5/server/api/service/IPendingServiceCallback.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.service; - -/** - * Callback that will be executed when the result of a pending service call - * has been received. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * - */ -public interface IPendingServiceCallback { - - /** - * Triggered when results are recieved - * - * @param call Call object this callback is applied to - */ - public void resultReceived(IPendingServiceCall call); - -} diff --git a/src/main/java/org/red5/server/api/service/IServiceCall.java b/src/main/java/org/red5/server/api/service/IServiceCall.java deleted file mode 100644 index 6005e5835..000000000 --- a/src/main/java/org/red5/server/api/service/IServiceCall.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.service; - -/** - * Container for a Service Call - */ -public interface IServiceCall { - - /** - * Whether call was successful or not - * - * @return true on success, false otherwise - */ - public abstract boolean isSuccess(); - - /** - * Returns service method name - * - * @return Service method name as string - */ - public abstract String getServiceMethodName(); - - /** - * Returns service name - * - * @return Service name - */ - public abstract String getServiceName(); - - /** - * Returns array of service method arguments - * - * @return array of service method arguments - */ - public abstract Object[] getArguments(); - - /** - * Get service call status - * - * @return service call status - */ - public abstract byte getStatus(); - - /** - * Returns the time stamp at which this object was deserialized. - * - * @return the readTime - */ - public long getReadTime(); - - /** - * Returns the time stamp at which this object was serialized. - * - * @return the writeTime - */ - public long getWriteTime(); - - /** - * Get service call exception - * - * @return service call exception - */ - public abstract Exception getException(); - - /** - * Sets status - * - * @param status Status as byte - */ - public abstract void setStatus(byte status); - - /** - * Sets exception - * - * @param exception Call exception - */ - public abstract void setException(Exception exception); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/service/IServiceCapableConnection.java b/src/main/java/org/red5/server/api/service/IServiceCapableConnection.java deleted file mode 100644 index 4e4f90f65..000000000 --- a/src/main/java/org/red5/server/api/service/IServiceCapableConnection.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.service; - -import org.red5.server.api.IConnection; -import org.red5.server.net.rtmp.status.Status; - -/** - * Connection that has options to invoke and handle remote calls - */ -// TODO: this should really extend IServiceInvoker -public interface IServiceCapableConnection extends IConnection { - /** - * Invokes service using remoting call object - * @param call Service call object - */ - void invoke(IServiceCall call); - - /** - * Invoke service using call and channel - * @param call Service call - * @param channel Channel used - */ - void invoke(IServiceCall call, int channel); - - /** - * Invoke method by name - * @param method Called method name - */ - void invoke(String method); - - /** - * Invoke method by name with callback - * @param method Called method name - * @param callback Callback - */ - void invoke(String method, IPendingServiceCallback callback); - - /** - * Invoke method with parameters - * @param method Method name - * @param params Invocation parameters passed to method - */ - void invoke(String method, Object[] params); - - /** - * - * @param method - * @param params - * @param callback - */ - void invoke(String method, Object[] params, IPendingServiceCallback callback); - - /** - * - * @param call - */ - void notify(IServiceCall call); - - /** - * - * @param call - * @param channel - */ - void notify(IServiceCall call, int channel); - - /** - * - * @param method - */ - void notify(String method); - - /** - * - * @param method - * @param params - */ - void notify(String method, Object[] params); - - /** - * Sends a status object to the connection - * @param status - */ - void status(Status status); - - /** - * Sends a status object to the connection on a given channel - * @param status - * @param channel - */ - void status(Status status, int channel); -} diff --git a/src/main/java/org/red5/server/api/service/IServiceHandlerProvider.java b/src/main/java/org/red5/server/api/service/IServiceHandlerProvider.java deleted file mode 100644 index 20cf93b5b..000000000 --- a/src/main/java/org/red5/server/api/service/IServiceHandlerProvider.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.service; - -import java.util.Set; - -/** - * Supports registration and lookup of service handlers. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * - */ -public interface IServiceHandlerProvider { - - /** - * Register an object that provides methods which can be called from a - * client. - * - *

- * Example:
- * If you registered a handler with the name "one.two" that - * provides a method "callMe", you can call a method - * "one.two.callMe" from the client.

- * - * @param name the name of the handler - * @param handler the handler object - */ - public void registerServiceHandler(String name, Object handler); - - /** - * Unregister service handler. - * - * @param name the name of the handler - */ - public void unregisterServiceHandler(String name); - - /** - * Return a previously registered service handler. - * - * @param name the name of the handler to return - * @return the previously registered handler - */ - public Object getServiceHandler(String name); - - /** - * Get list of registered service handler names. - * - * @return the names of the registered handlers - */ - public Set getServiceHandlerNames(); - -} diff --git a/src/main/java/org/red5/server/api/service/IServiceInvoker.java b/src/main/java/org/red5/server/api/service/IServiceInvoker.java deleted file mode 100644 index d24f37145..000000000 --- a/src/main/java/org/red5/server/api/service/IServiceInvoker.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.service; - -import org.red5.server.api.scope.IScope; - -/** - * Interface for objects that execute service calls (remote calls from client). - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - */ -public interface IServiceInvoker { - - /** - * Execute the passed service call in the given scope. This looks up the - * handler for the call in the scope and the context of the scope. - * - * @param call - * the call to invoke - * @param scope - * the scope to search for a handler - * @return true if the call was performed, otherwise false - */ - boolean invoke(IServiceCall call, IScope scope); - - /** - * Execute the passed service call in the given object. - * - * @param call - * the call to invoke - * @param service - * the service to use - * @return true if the call was performed, otherwise false - */ - boolean invoke(IServiceCall call, Object service); - -} diff --git a/src/main/java/org/red5/server/api/service/IStreamSecurityService.java b/src/main/java/org/red5/server/api/service/IStreamSecurityService.java deleted file mode 100644 index d21b189b5..000000000 --- a/src/main/java/org/red5/server/api/service/IStreamSecurityService.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.service; - -import java.util.Set; - -import org.red5.server.api.scope.IScopeService; -import org.red5.server.api.stream.IStreamPlaybackSecurity; -import org.red5.server.api.stream.IStreamPublishSecurity; - -/** - * Service that supports protecting access to streams. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IStreamSecurityService extends IScopeService { - - /** - * Name of a bean defining that scope service. - * */ - public static final String BEAN_NAME = "streamSecurityService"; - - /** - * Add handler that protects stream publishing. - * - * @param handler Handler to add. - */ - public void registerStreamPublishSecurity(IStreamPublishSecurity handler); - - /** - * Remove handler that protects stream publishing. - * - * @param handler Handler to remove. - */ - public void unregisterStreamPublishSecurity(IStreamPublishSecurity handler); - - /** - * Get handlers that protect stream publishing. - * - * @return list of handlers - */ - public Set getStreamPublishSecurity(); - - /** - * Add handler that protects stream playback. - * - * @param handler Handler to add. - */ - public void registerStreamPlaybackSecurity(IStreamPlaybackSecurity handler); - - /** - * Remove handler that protects stream playback. - * - * @param handler Handler to remove. - */ - public void unregisterStreamPlaybackSecurity(IStreamPlaybackSecurity handler); - - /** - * Get handlers that protect stream plaback. - * - * @return list of handlers - */ - public Set getStreamPlaybackSecurity(); - -} diff --git a/src/main/java/org/red5/server/api/service/IStreamableFileService.java b/src/main/java/org/red5/server/api/service/IStreamableFileService.java deleted file mode 100644 index 45679567b..000000000 --- a/src/main/java/org/red5/server/api/service/IStreamableFileService.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.service; - -import java.io.File; -import java.io.IOException; - -import org.red5.io.IStreamableFile; - -/** - * Provides access to files that can be streamed. - */ -public interface IStreamableFileService { - - /** - * Sets the prefix. - * - * @param prefix - */ - public void setPrefix(String prefix); - - /** - * Getter for prefix. Prefix is used in filename composition to fetch real file name. - * - * @return Prefix - */ - public String getPrefix(); - - /** - * Sets the file extensions serviced. If there are more than one, they are separated - * by commas. - * - * @param extension - */ - public void setExtension(String extension); - - /** - * Getter for extension of file - * - * @return File extension that is used - */ - public String getExtension(); - - /** - * Prepair given string to conform filename requirements, for example, add - * extension to the end if missing. - * @param name String to format - * @return Correct filename - */ - public String prepareFilename(String name); - - /** - * Check whether file can be used by file service, that is, it does exist and have valid extension - * @param file File object - * @return true if file exist and has valid extension, - * false otherwise - */ - public boolean canHandle(File file); - - /** - * Return streamable file reference. For FLV files returned streamable file already has - * generated metadata injected. - * - * @param file File resource - * @return Streamable file resource - * @throws IOException Thrown if there were problems accessing given file - */ - public IStreamableFile getStreamableFile(File file) throws IOException; - -} diff --git a/src/main/java/org/red5/server/api/so/IClientSharedObject.java b/src/main/java/org/red5/server/api/so/IClientSharedObject.java deleted file mode 100644 index e526ad2bd..000000000 --- a/src/main/java/org/red5/server/api/so/IClientSharedObject.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.so; - -import org.red5.server.api.IConnection; - -/** - * Clientside access to shared objects. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ - -public interface IClientSharedObject extends ISharedObjectBase { - - /** - * Connect the shared object using the passed connection. - * - * @param conn connect to connect to - */ - public void connect(IConnection conn); - - /** - * Check if the shared object is connected to the server. - * - * @return is connected - */ - public boolean isConnected(); - - /** - * Disconnect the shared object. - */ - public void disconnect(); - -} diff --git a/src/main/java/org/red5/server/api/so/ISharedObject.java b/src/main/java/org/red5/server/api/so/ISharedObject.java deleted file mode 100644 index a7bd30780..000000000 --- a/src/main/java/org/red5/server/api/so/ISharedObject.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.so; - -import org.red5.server.api.scope.IBasicScope; -import org.red5.server.api.statistics.ISharedObjectStatistics; - -/** - * Serverside access to shared objects. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ - -public interface ISharedObject extends IBasicScope, ISharedObjectBase, ISharedObjectSecurityService { - - /** - * Prevent shared object from being released. Each call to acquire - * must be paired with a call to release so the SO isn't held - * forever. - * - * This method basically is a noop for persistent SOs as their data is stored - * and they can be released without losing their contents. - */ - public void acquire(); - - /** - * Check if shared object currently is acquired. - * - * @return true if the SO is acquired, otherwise false - */ - public boolean isAcquired(); - - /** - * Release previously acquired shared object. If the SO is non-persistent, - * no more clients are connected the SO isn't acquired any more, the data - * is released. - */ - public void release(); - - /** - * Return statistics about the shared object. - * - * @return statistics - */ - public ISharedObjectStatistics getStatistics(); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/so/ISharedObjectBase.java b/src/main/java/org/red5/server/api/so/ISharedObjectBase.java deleted file mode 100644 index 993c8af31..000000000 --- a/src/main/java/org/red5/server/api/so/ISharedObjectBase.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.so; - -import java.util.List; -import java.util.Map; - -import org.red5.server.api.ICastingAttributeStore; -import org.red5.server.api.event.IEventListener; - -/** - * Base interface for shared objects. Changes to the shared objects are - * propagated to all subscribed clients. - * - * If you want to modify multiple attributes and notify the clients about all - * changes at once, you can use code like this: - *

- * - * SharedObject.beginUpdate();
- * SharedObject.setAttribute("One", '1');
- * SharedObject.setAttribute("Two", '2');
- * SharedObject.removeAttribute("Three");
- * SharedObject.endUpdate();
- *
- *

- * - * All changes between "beginUpdate" and "endUpdate" will be sent to the clients - * using one notification event. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ - -public interface ISharedObjectBase extends ISharedObjectHandlerProvider, ICastingAttributeStore { - - /** - * Returns the version of the shared object. The version is incremented - * automatically on each modification. - * - * @return the version of the shared object - */ - public int getVersion(); - - /** - * Check if the object has been created as persistent shared object by the - * client. - * - * @return true if the shared object is persistent, false otherwise - */ - public boolean isPersistent(); - - /** - * Return a map containing all attributes of the shared object.
- * NOTE: The returned map will be read-only. - * - * @return a map containing all attributes of the shared object - */ - public Map getData(); - - /** - * Send a message to a handler of the shared object. - * - * @param handler the name of the handler to call - * @param arguments a list of objects that should be passed as arguments to the - * handler - */ - public void sendMessage(String handler, List arguments); - - /** - * Start performing multiple updates to the shared object from serverside - * code. - */ - public void beginUpdate(); - - /** - * Start performing multiple updates to the shared object from a connected - * client. - * @param source Update events listener - */ - public void beginUpdate(IEventListener source); - - /** - * The multiple updates are complete, notify clients about all changes at - * once. - */ - public void endUpdate(); - - /** - * Register object that will be notified about update events. - * - * @param listener the object to notify - */ - public void addSharedObjectListener(ISharedObjectListener listener); - - /** - * Unregister object to not longer receive update events. - * - * @param listener the object to unregister - */ - public void removeSharedObjectListener(ISharedObjectListener listener); - - /** - * Locks the shared object instance. Prevents any changes to this object by - * clients until the SharedObject.unlock() method is called. - */ - public void lock(); - - /** - * Unlocks a shared object instance that was locked with - * SharedObject.lock(). - */ - public void unlock(); - - /** - * Returns the locked state of this SharedObject. - * - * @return true if in a locked state; false otherwise - */ - public boolean isLocked(); - - /** - * Deletes all the attributes and sends a clear event to all listeners. The - * persistent data object is also removed from a persistent shared object. - * - * @return true if successful; false otherwise - */ - public boolean clear(); - - /** - * Detaches a reference from this shared object, this will destroy the - * reference immediately. This is useful when you don't want to proxy a - * shared object any longer. - */ - public void close(); - -} diff --git a/src/main/java/org/red5/server/api/so/ISharedObjectHandlerProvider.java b/src/main/java/org/red5/server/api/so/ISharedObjectHandlerProvider.java deleted file mode 100644 index 23b320b13..000000000 --- a/src/main/java/org/red5/server/api/so/ISharedObjectHandlerProvider.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.so; - -import org.red5.server.api.service.IServiceHandlerProvider; - -/** - * Supports registration and lookup of shared object handlers. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * - */ -public interface ISharedObjectHandlerProvider extends IServiceHandlerProvider { - - /** - * Register an object that provides methods which handle calls without - * a service name to a shared object. - * - * @param handler the handler object - */ - public void registerServiceHandler(Object handler); - - /** - * Unregister the shared object handler for calls without a service name. - */ - public void unregisterServiceHandler(String name); - -} diff --git a/src/main/java/org/red5/server/api/so/ISharedObjectListener.java b/src/main/java/org/red5/server/api/so/ISharedObjectListener.java deleted file mode 100644 index c7cf33090..000000000 --- a/src/main/java/org/red5/server/api/so/ISharedObjectListener.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.so; - -import java.util.List; -import java.util.Map; - -import org.red5.server.api.IAttributeStore; - -/** - * Notifications about shared object updates. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface ISharedObjectListener { - - /** - * Called when a client connects to a shared object. - * - * @param so - * the shared object - */ - void onSharedObjectConnect(ISharedObjectBase so); - - /** - * Called when a client disconnects from a shared object. - * - * @param so - * the shared object - */ - void onSharedObjectDisconnect(ISharedObjectBase so); - - /** - * Called when a shared object attribute is updated. - * - * @param so - * the shared object - * @param key - * the name of the attribute - * @param value - * the value of the attribute - */ - void onSharedObjectUpdate(ISharedObjectBase so, String key, Object value); - - /** - * Called when multiple attributes of a shared object are updated. - * - * @param so - * the shared object - * @param values - * the new attributes of the shared object - */ - void onSharedObjectUpdate(ISharedObjectBase so, IAttributeStore values); - - /** - * Called when multiple attributes of a shared object are updated. - * - * @param so - * the shared object - * @param values - * the new attributes of the shared object - */ - void onSharedObjectUpdate(ISharedObjectBase so, Map values); - - /** - * Called when an attribute is deleted from the shared object. - * - * @param so - * the shared object - * @param key - * the name of the attribute to delete - */ - void onSharedObjectDelete(ISharedObjectBase so, String key); - - /** - * Called when all attributes of a shared object are removed. - * - * @param so - * the shared object - */ - void onSharedObjectClear(ISharedObjectBase so); - - /** - * Called when a shared object method call is sent. - * - * @param so - * the shared object - * @param method - * the method name to call - * @param params - * the arguments - */ - void onSharedObjectSend(ISharedObjectBase so, String method, List params); - -} diff --git a/src/main/java/org/red5/server/api/so/ISharedObjectSecurity.java b/src/main/java/org/red5/server/api/so/ISharedObjectSecurity.java deleted file mode 100644 index 89024cc30..000000000 --- a/src/main/java/org/red5/server/api/so/ISharedObjectSecurity.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.so; - -import java.util.List; - -import org.red5.server.api.scope.IScope; - -/** - * Interface for handlers that control access to shared objects. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface ISharedObjectSecurity { - - /** - * Check if the a shared object may be created in the given scope. - * - * @param scope scope - * @param name name - * @param persistent is persistent - * @return is creation allowed - */ - public boolean isCreationAllowed(IScope scope, String name, boolean persistent); - - /** - * Check if a connection to the given existing shared object is allowed. - * - * @param so shared ojbect - * @return is connection alowed - */ - public boolean isConnectionAllowed(ISharedObject so); - - /** - * Check if a modification is allowed on the given shared object. - * - * @param so shared object - * @param key key - * @param value value - * @return true if given key is modifiable; false otherwise - */ - public boolean isWriteAllowed(ISharedObject so, String key, Object value); - - /** - * Check if the deletion of a property is allowed on the given shared object. - * - * @param so shared object - * @param key key - * @return true if delete allowed; false otherwise - */ - public boolean isDeleteAllowed(ISharedObject so, String key); - - /** - * Check if sending a message to the shared object is allowed. - * - * @param so shared object - * @param message message - * @param arguments arguments - * @return true if allowed - */ - public boolean isSendAllowed(ISharedObject so, String message, List arguments); - -} diff --git a/src/main/java/org/red5/server/api/so/ISharedObjectSecurityService.java b/src/main/java/org/red5/server/api/so/ISharedObjectSecurityService.java deleted file mode 100644 index 9493b5bbc..000000000 --- a/src/main/java/org/red5/server/api/so/ISharedObjectSecurityService.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.so; - -import java.util.Set; - -import org.red5.server.api.scope.IScopeService; - -/** - * Service that supports protecting access to shared objects. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface ISharedObjectSecurityService extends IScopeService { - - /** - * Name of a bean defining that scope service. - * */ - public static final String BEAN_NAME = "sharedObjectSecurityService"; - - /** - * Add handler that protects shared objects. - * - * @param handler Handler to add. - */ - public void registerSharedObjectSecurity(ISharedObjectSecurity handler); - - /** - * Remove handler that protects shared objects. - * - * @param handler Handler to remove. - */ - public void unregisterSharedObjectSecurity(ISharedObjectSecurity handler); - - /** - * Get handlers that protect shared objects. - * - * @return list of handlers - */ - public Set getSharedObjectSecurity(); - -} diff --git a/src/main/java/org/red5/server/api/so/ISharedObjectService.java b/src/main/java/org/red5/server/api/so/ISharedObjectService.java deleted file mode 100644 index 775f1daf4..000000000 --- a/src/main/java/org/red5/server/api/so/ISharedObjectService.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.so; - -import java.util.Set; - -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeService; - -/** - * Service that manages shared objects for given scope. - * - */ -public interface ISharedObjectService extends IScopeService { - - public static String BEAN_NAME = "sharedObjectService"; - - /** - * Get a set of the shared object names. - * - * @param scope the scope to return the shared object names from - * @return set containing the shared object names - */ - public Set getSharedObjectNames(IScope scope); - - /** - * Create a new shared object. - * - * @param scope the scope to create the shared object in - * @param name the name of the shared object - * @param persistent will the shared object be persistent - * @return true if the shared object was created or already exists, otherwise - * false - */ - public boolean createSharedObject(IScope scope, String name, boolean persistent); - - /** - * Get a shared object by name. - * - * @param scope the scope to get the shared object from - * @param name the name of the shared object - * @return shared object, or null if not found - */ - public ISharedObject getSharedObject(IScope scope, String name); - - /** - * Get a shared object by name and create it if it doesn't exist. - * - * @param scope the scope to get the shared object from - * @param name the name of the shared object - * @param persistent should the shared object be created persistent - * @return the shared object - */ - public ISharedObject getSharedObject(IScope scope, String name, boolean persistent); - - /** - * Check if a shared object exists. - * - * @param scope the scope to check for the shared object - * @param name the name of the shared object - * @return true if the shared object exists, otherwise - * false - */ - public boolean hasSharedObject(IScope scope, String name); - - /** - *

- * Deletes persistent shared objects specified by name and clears all - * properties from active shared objects (persistent and nonpersistent). The - * name parameter specifies the name of a shared object, which can include a - * slash (/) as a delimiter between directories in the path. The last - * element in the path can contain wildcard patterns (for example, a - * question mark [?] and an asterisk [*]) or a shared object name. The - * clearSharedObjects() method traverses the shared object hierarchy along - * the specified path and clears all the shared objects. Specifying a slash - * (/) clears all the shared objects associated with an application - * instance. - *

- *

- * The following values are possible for the soPath parameter:
/ - * clears all local and persistent shared objects associated with the - * instance.
- * /foo/bar clears the shared object /foo/bar; if bar is a directory name, - * no shared objects are deleted.
- * /foo/bar/* clears all shared objects stored under the instance directory - * /foo/bar. The bar directory is also deleted if no persistent shared - * objects are in use within this namespace.
- * /foo/bar/XX?? clears all shared objects that begin with XX, followed by - * any two characters. If a directory name matches this specification, all - * the shared objects within this directory are cleared. - *

- *

- * If you call the clearSharedObjects() method and the specified path - * matches a shared object that is currently active, all its properties are - * deleted, and a "clear" event is sent to all subscribers of the shared - * object. If it is a persistent shared object, the persistent store is also - * cleared. - *

- *
- * - * @param scope the scope to check for the shared object - * @param name the name of the shared object - * @return true if the shared object at the specified path was deleted; - * otherwise, false. If using wildcard characters to delete multiple - * files, the method returns true only if all the shared objects - * matching the wildcard pattern were successfully deleted; - * otherwise, it will return false. - */ - public boolean clearSharedObjects(IScope scope, String name); - -} diff --git a/src/main/java/org/red5/server/api/statistics/IClientBroadcastStreamStatistics.java b/src/main/java/org/red5/server/api/statistics/IClientBroadcastStreamStatistics.java deleted file mode 100644 index 473ef3b13..000000000 --- a/src/main/java/org/red5/server/api/statistics/IClientBroadcastStreamStatistics.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.statistics; - -/** - * Statistical informations about a stream that is broadcasted by a client. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IClientBroadcastStreamStatistics extends IStreamStatistics { - - /** - * Get the filename the stream is being saved as. - * - * @return The filename relative to the scope or null - * if the stream is not being saved. - */ - public String getSaveFilename(); - - /** - * Get stream publish name. Publish name is the value of the first parameter - * had been passed to NetStream.publish on client side in - * SWF. - * - * @return Stream publish name - */ - public String getPublishedName(); - - /** - * Return total number of subscribers. - * - * @return number of subscribers - */ - public int getTotalSubscribers(); - - /** - * Return maximum number of concurrent subscribers. - * - * @return number of subscribers - */ - public int getMaxSubscribers(); - - /** - * Return current number of subscribers. - * - * @return number of subscribers - */ - public int getActiveSubscribers(); - - /** - * Return total number of bytes received from client for this stream. - * - * @return number of bytes - */ - public long getBytesReceived(); - -} diff --git a/src/main/java/org/red5/server/api/statistics/IPlaylistSubscriberStreamStatistics.java b/src/main/java/org/red5/server/api/statistics/IPlaylistSubscriberStreamStatistics.java deleted file mode 100644 index df402bdd1..000000000 --- a/src/main/java/org/red5/server/api/statistics/IPlaylistSubscriberStreamStatistics.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.statistics; - -/** - * Statistical informations about a stream that is subscribed by a client. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IPlaylistSubscriberStreamStatistics extends IStreamStatistics { - - /** - * Return total number of bytes sent to the client from this stream. - * - * @return number of bytes - */ - public long getBytesSent(); - - /** - * Return the buffer duration as requested by the client. - * - * @return the buffer duration in milliseconds - */ - public int getClientBufferDuration(); - - /** - * Return estimated fill ratio of the client buffer. - * - * @return fill ratio in percent - */ - public double getEstimatedBufferFill(); - -} diff --git a/src/main/java/org/red5/server/api/statistics/IScopeStatistics.java b/src/main/java/org/red5/server/api/statistics/IScopeStatistics.java deleted file mode 100644 index ab99614c2..000000000 --- a/src/main/java/org/red5/server/api/statistics/IScopeStatistics.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.statistics; - -/** - * Statistical informations about a scope. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IScopeStatistics extends IStatisticsBase { - - /** - * Get the name of this scope. Eg. someroom. - * - * @return the name - */ - public String getName(); - - /** - * Get the full absolute path. Eg. host/myapp/someroom. - * - * @return Absolute scope path - */ - public String getPath(); - - /** - * Get the scopes depth, how far down the scope tree is it. The lowest depth - * is 0x00, the depth of Global scope. Application scope depth is 0x01. Room - * depth is 0x02, 0x03 and so forth. - * - * @return the depth - */ - public int getDepth(); - - /** - * Return total number of connections to the scope. - * - * @return number of connections - */ - public int getTotalConnections(); - - /** - * Return maximum number of concurrent connections to the scope. - * - * @return number of connections - */ - public int getMaxConnections(); - - /** - * Return current number of connections to the scope. - * - * @return number of connections - */ - public int getActiveConnections(); - - /** - * Return total number of clients connected to the scope. - * - * @return number of clients - */ - public int getTotalClients(); - - /** - * Return maximum number of clients concurrently connected to the scope. - * - * @return number of clients - */ - public int getMaxClients(); - - /** - * Return current number of clients connected to the scope. - * - * @return number of clients - */ - public int getActiveClients(); - - /** - * Return total number of subscopes created. - * - * @return number of subscopes created - */ - public int getTotalSubscopes(); - - /** - * Return maximum number of concurrently existing subscopes. - * - * @return number of subscopes - */ - public int getMaxSubscopes(); - - /** - * Return number of currently existing subscopes. - * - * @return number of subscopes - */ - public int getActiveSubscopes(); - -} diff --git a/src/main/java/org/red5/server/api/statistics/ISharedObjectStatistics.java b/src/main/java/org/red5/server/api/statistics/ISharedObjectStatistics.java deleted file mode 100644 index 530b30e32..000000000 --- a/src/main/java/org/red5/server/api/statistics/ISharedObjectStatistics.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.statistics; - -/** - * Statistics informations about a shared object. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface ISharedObjectStatistics extends IStatisticsBase { - - /** - * Return the name of the shared object. - * - * @return the name of the shared object - */ - public String getName(); - - /** - * Check if the shared object is persistent. - * - * @return True if the shared object is persistent, otherwise False - */ - public boolean isPersistent(); - - /** - * Return the version number of the shared object. - * - * @return the version - */ - public int getVersion(); - - /** - * Return total number of subscribed listeners. - * - * @return number of listeners - */ - public int getTotalListeners(); - - /** - * Return maximum number of concurrent subscribed listenes. - * - * @return number of listeners - */ - public int getMaxListeners(); - - /** - * Return current number of subscribed listeners. - * - * @return number of listeners - */ - public int getActiveListeners(); - - /** - * Return number of attribute changes. - * - * @return number of changes - */ - public int getTotalChanges(); - - /** - * Return number of attribute deletes. - * - * @return number of deletes - */ - public int getTotalDeletes(); - - /** - * Return number of times a message was sent. - * - * @return number of sends - */ - public int getTotalSends(); - -} diff --git a/src/main/java/org/red5/server/api/statistics/IStatisticsBase.java b/src/main/java/org/red5/server/api/statistics/IStatisticsBase.java deleted file mode 100644 index edb096907..000000000 --- a/src/main/java/org/red5/server/api/statistics/IStatisticsBase.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.statistics; - -/** - * Base class for all statistics informations. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IStatisticsBase { - - /** - * Return the timestamp the object was created. - * - * @return the timestamp in milliseconds since midnight, January 1, 1970 UTC. - */ - public long getCreationTime(); - -} diff --git a/src/main/java/org/red5/server/api/statistics/IStreamStatistics.java b/src/main/java/org/red5/server/api/statistics/IStreamStatistics.java deleted file mode 100644 index 45c0bc7a2..000000000 --- a/src/main/java/org/red5/server/api/statistics/IStreamStatistics.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.statistics; - -/** - * Base class for all stream statistics. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IStreamStatistics extends IStatisticsBase { - - /** - * Return the currently active timestamp inside the stream. - * - * @return the timestamp in milliseconds - */ - public int getCurrentTimestamp(); - -} diff --git a/src/main/java/org/red5/server/api/statistics/support/StatisticsCounter.java b/src/main/java/org/red5/server/api/statistics/support/StatisticsCounter.java deleted file mode 100644 index 8bf352524..000000000 --- a/src/main/java/org/red5/server/api/statistics/support/StatisticsCounter.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.statistics.support; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Counts numbers used by the statistics. Keeps track of current, - * maximum and total numbers. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public class StatisticsCounter { - - /** Current number. */ - private AtomicInteger current = new AtomicInteger(); - - /** Total number. */ - private AtomicInteger total = new AtomicInteger(); - - /** Maximum number. */ - private AtomicInteger max = new AtomicInteger(); - - /** - * Increment statistics by one. - */ - public void increment() { - total.incrementAndGet(); - max.compareAndSet(current.intValue(), current.incrementAndGet()); - } - - /** - * Decrement statistics by one. - */ - public void decrement() { - current.decrementAndGet(); - } - - /** - * Get current number. - * - * @return current number - */ - public int getCurrent() { - return current.intValue(); - } - - /** - * Get total number. - * - * @return total - */ - public int getTotal() { - return total.intValue(); - } - - /** - * Get maximum number. - * - * @return max - */ - public int getMax() { - return max.intValue(); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "StatisticsCounter [current=" + current + ", total=" + total + ", max=" + max + "]"; - } - -} diff --git a/src/main/java/org/red5/server/api/stream/IBroadcastStream.java b/src/main/java/org/red5/server/api/stream/IBroadcastStream.java deleted file mode 100644 index 6617d22ec..000000000 --- a/src/main/java/org/red5/server/api/stream/IBroadcastStream.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import java.io.IOException; -import java.util.Collection; - -import org.red5.server.messaging.IProvider; -import org.red5.server.net.rtmp.event.Notify; - -/** - * A broadcast stream is a stream source to be subscribed to by clients. To - * subscribe to a stream from your client Flash application use NetStream.play - * method. Broadcast stream can be saved at the server-side. - * - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IBroadcastStream extends IStream { - - /** - * Save the broadcast stream as a file. - * - * @param filePath - * The path of the file relative to the scope. - * @param isAppend - * Whether to append to the end of file. - * @throws IOException - * File could not be created/written to. - * @throws ResourceExistException - * Resource exist when trying to create. - * @throws ResourceNotFoundException - * Resource not exist when trying to append. - */ - void saveAs(String filePath, boolean isAppend) - throws IOException, ResourceNotFoundException, ResourceExistException; - - /** - * Get the filename the stream is being saved as. - * - * @return The filename relative to the scope or null - * if the stream is not being saved. - */ - String getSaveFilename(); - - /** - * Get the provider corresponding to this stream. Provider objects are - * object that - * - * @return the provider - */ - IProvider getProvider(); - - /** - * Get stream publish name. Publish name is the value of the first parameter - * had been passed to NetStream.publish on client side in - * SWF. - * - * @return Stream publish name - */ - String getPublishedName(); - - /** - * - * @param name - * Set stream publish name - */ - void setPublishedName(String name); - - /** - * Add a listener to be notified about received packets. - * - * @param listener the listener to add - */ - public void addStreamListener(IStreamListener listener); - - /** - * Remove a listener from being notified about received packets. - * - * @param listener the listener to remove - */ - public void removeStreamListener(IStreamListener listener); - - /** - * Return registered stream listeners. - * - * @return the registered listeners - */ - public Collection getStreamListeners(); - - /** - * Returns the metadata for the associated stream, if it exists. - * - * @return stream meta data - */ - public Notify getMetaData(); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/stream/IClientBroadcastStream.java b/src/main/java/org/red5/server/api/stream/IClientBroadcastStream.java deleted file mode 100644 index cee9f8b33..000000000 --- a/src/main/java/org/red5/server/api/stream/IClientBroadcastStream.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import java.util.Map; -import org.red5.server.api.statistics.IClientBroadcastStreamStatistics; - -/** - * A broadcast stream that comes from client. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - * @author Paul Gregoire (mondain@gmail.com) - */ -public interface IClientBroadcastStream extends IClientStream, IBroadcastStream { - - /** - * Notify client that stream is ready for publishing. - */ - public void startPublishing(); - - /** - * Return statistics about the stream. - * - * @return statistics - */ - public IClientBroadcastStreamStatistics getStatistics(); - - /** - * Sets streaming parameters as supplied by the publishing application. - * - * @param params - */ - public void setParameters(Map params); - - /** - * Returns streaming parameters. - * - * @return parameters - */ - public Map getParameters(); - -} diff --git a/src/main/java/org/red5/server/api/stream/IClientStream.java b/src/main/java/org/red5/server/api/stream/IClientStream.java deleted file mode 100644 index 4e1cad7b7..000000000 --- a/src/main/java/org/red5/server/api/stream/IClientStream.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -/** - * A stream that is bound to a client. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IClientStream extends IStream { - - public static final String MODE_READ = "read"; - - public static final String MODE_RECORD = "record"; - - public static final String MODE_APPEND = "append"; - - public static final String MODE_LIVE = "live"; - - public static final String MODE_PUBLISH = "publish"; - - /** - * Get stream id allocated in a connection. - * - * @return the stream id - */ - int getStreamId(); - - /** - * Get connection containing the stream. - * - * @return the connection object or null if the connection is no longer active - */ - IStreamCapableConnection getConnection(); - - /** - * Set the buffer duration for this stream as requested by the client. - * - * @param bufferTime duration in ms the client wants to buffer - */ - void setClientBufferDuration(int bufferTime); - - /** - * Get the buffer duration for this stream as requested by the client. - * - * @return bufferTime duration in ms the client wants to buffer - */ - int getClientBufferDuration(); - - /** - * Set the published stream name that this client is consuming. - * - * @param streamName of stream being consumed - */ - void setBroadcastStreamPublishName(String streamName); - - /** - * Returns the published stream name that this client is consuming. - * - * @return stream name of stream being consumed - */ - String getBroadcastStreamPublishName(); - -} diff --git a/src/main/java/org/red5/server/api/stream/IPlayItem.java b/src/main/java/org/red5/server/api/stream/IPlayItem.java deleted file mode 100644 index 4a3831da4..000000000 --- a/src/main/java/org/red5/server/api/stream/IPlayItem.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.server.messaging.IMessageInput; - -/** - * Playlist item. Each playlist item has name, start time, length in milliseconds and - * message input source. - */ -public interface IPlayItem { - - /** - * Get name of item. - * The VOD or Live stream provider is found according to this name. - * @return the name - */ - String getName(); - - /** - * Start time in milliseconds. - * - * @return start time - */ - long getStart(); - - /** - * Play length in milliseconds. - * - * @return length in milliseconds - */ - long getLength(); - - /** - * Get a message input for play. - * This object overrides the default algorithm for finding the appropriate VOD or Live stream provider according to - * the item name. - * - * @return message input - */ - IMessageInput getMessageInput(); -} diff --git a/src/main/java/org/red5/server/api/stream/IPlaylist.java b/src/main/java/org/red5/server/api/stream/IPlaylist.java deleted file mode 100644 index bdb5caa84..000000000 --- a/src/main/java/org/red5/server/api/stream/IPlaylist.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -/** - * Playlist - */ -public interface IPlaylist { - /** - * Add an item to the list. - * - * @param item Playlist item - */ - void addItem(IPlayItem item); - - /** - * Add an item to specific index. - * - * @param item Playlist item - * @param index Index in list - */ - void addItem(IPlayItem item, int index); - - /** - * Remove an item from list. - * - * @param index Index in list - */ - void removeItem(int index); - - /** - * Remove all items. - */ - void removeAllItems(); - - /** - * Return number of items in list - * - * @return Number of items in list - */ - int getItemSize(); - - /** - * Get currently playing item index. - * @return Currently playing item index. - */ - int getCurrentItemIndex(); - - /** - * Get currently playing item - * @return Item - */ - IPlayItem getCurrentItem(); - - /** - * Get the item according to the index. - * @param index Item index - * @return Item at that index in list - */ - IPlayItem getItem(int index); - - /** - * Check if the playlist has more items after the currently - * playing one. - * - * @return true if more items are available, false otherwise - */ - boolean hasMoreItems(); - - /** - * Go for the previous played item. - */ - void previousItem(); - - /** - * Go for next item decided by controller logic. - */ - void nextItem(); - - /** - * Set the current item for playing. - * - * @param index Position in list - */ - void setItem(int index); - - /** - * Whether items are randomly played. - * - * @return true if shuffle is on for this list, false otherwise - */ - boolean isRandom(); - - /** - * Set whether items should be randomly played. - * - * @param random Shuffle flag - */ - void setRandom(boolean random); - - /** - * Whether rewind the list. - * - * @return true if playlist is rewind on end, false otherwise - */ - boolean isRewind(); - - /** - * Set whether rewind the list. - * - * @param rewind New vallue for rewind flag - */ - void setRewind(boolean rewind); - - /** - * Whether repeat playing an item. - * - * @return true if repeat mode is on for this playlist, false otherwise - */ - boolean isRepeat(); - - /** - * Set whether repeat playing an item. - * - * @param repeat New value for item playback repeat flag - */ - void setRepeat(boolean repeat); - - /** - * Set list controller. - * - * @param controller Playlist controller - */ - void setPlaylistController(IPlaylistController controller); -} diff --git a/src/main/java/org/red5/server/api/stream/IPlaylistController.java b/src/main/java/org/red5/server/api/stream/IPlaylistController.java deleted file mode 100644 index 0d3ce8b95..000000000 --- a/src/main/java/org/red5/server/api/stream/IPlaylistController.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -/** - * A play list controller that controls the order of play items. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IPlaylistController { - /** - * Get next item to play. - * - * @param playlist - * The related play list. - * @param itemIndex - * The current item index. -1 indicates to retrieve - * the first item for play. - * @return The next item index to play. -1 reaches the end. - */ - int nextItem(IPlaylist playlist, int itemIndex); - - /** - * Get previous item to play. - * - * @param playlist - * The related play list. - * @param itemIndex - * The current item index. IPlaylist.itemSize - * indicated to retrieve the last item for play. - * @return The previous item index to play. -1 reaches the - * beginning. - */ - int previousItem(IPlaylist playlist, int itemIndex); -} diff --git a/src/main/java/org/red5/server/api/stream/IPlaylistSubscriberStream.java b/src/main/java/org/red5/server/api/stream/IPlaylistSubscriberStream.java deleted file mode 100644 index 682899c65..000000000 --- a/src/main/java/org/red5/server/api/stream/IPlaylistSubscriberStream.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.server.api.statistics.IPlaylistSubscriberStreamStatistics; - -/** - * IPlaylistSubscriberStream has methods of both ISubscriberStream and IPlaylist - * but adds nothing new - */ -public interface IPlaylistSubscriberStream extends ISubscriberStream, IPlaylist { - - /** - * Return statistics about this stream. - * - * @return statistics - */ - public IPlaylistSubscriberStreamStatistics getStatistics(); - - /** - * Handles a change occurring on the stream. - * - * @param state stream state that we are changing to or notifying of - * @param changed changed items - */ - public void onChange(StreamState state, Object... changed); - - /** - * Replaces an item in the list with another item. - * - * @param oldItem - * @param newItem - * @return true if successful and false otherwise - */ - public boolean replace(IPlayItem oldItem, IPlayItem newItem); - -} diff --git a/src/main/java/org/red5/server/api/stream/IRtmpSampleAccess.java b/src/main/java/org/red5/server/api/stream/IRtmpSampleAccess.java deleted file mode 100644 index a8935b247..000000000 --- a/src/main/java/org/red5/server/api/stream/IRtmpSampleAccess.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.server.api.scope.IScope; - -public interface IRtmpSampleAccess { - - public static String BEAN_NAME = "rtmpSampleAccess"; - - /** - * Return true if sample access allowed on audio stream - * @param scope - * @return true if sample access allowed on audio stream - */ - public boolean isAudioAllowed(IScope scope); - - /** - * Return true if sample access allowed on video stream - * @param scope - * @return true if sample access allowed on video stream - */ - public boolean isVideoAllowed(IScope scope); - -} diff --git a/src/main/java/org/red5/server/api/stream/ISingleItemSubscriberStream.java b/src/main/java/org/red5/server/api/stream/ISingleItemSubscriberStream.java deleted file mode 100644 index bfef4477a..000000000 --- a/src/main/java/org/red5/server/api/stream/ISingleItemSubscriberStream.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -/** - * A subscriber stream that has only one item for play. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface ISingleItemSubscriberStream extends ISubscriberStream { - /** - * Setter for property 'playItem'. - * - * @param item Value to set for property 'playItem'. - */ - void setPlayItem(IPlayItem item); -} diff --git a/src/main/java/org/red5/server/api/stream/IStream.java b/src/main/java/org/red5/server/api/stream/IStream.java deleted file mode 100644 index 5b862aeab..000000000 --- a/src/main/java/org/red5/server/api/stream/IStream.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.codec.IStreamCodecInfo; -import org.red5.server.api.scope.IScope; - -/** - * Base interface for stream objects. - * A stream object is always associated with a scope. - */ -public interface IStream { - - /** - * Get the name of the stream. The name is unique across the server. This is - * just an id of the stream and NOT the name that is used at client side to - * subscribe to the stream. For that name, use - * {@link IBroadcastStream#getPublishedName()} - * - * @return the name of the stream - */ - public String getName(); - - /** - * Get Codec info for a stream. - * - * @return codec info - */ - IStreamCodecInfo getCodecInfo(); - - /** - * Get the scope this stream is associated with. - * - * @return scope object - */ - public IScope getScope(); - - /** - * Start this stream. - */ - public void start(); - - /** - * Stop this stream. - */ - public void stop(); - - /** - * Close this stream. - */ - public void close(); - - /** - * Returns the timestamp at which the stream was created. - * - * @return creation timestamp - */ - public long getCreationTime(); - -} diff --git a/src/main/java/org/red5/server/api/stream/IStreamAwareScopeHandler.java b/src/main/java/org/red5/server/api/stream/IStreamAwareScopeHandler.java deleted file mode 100644 index bbe24934f..000000000 --- a/src/main/java/org/red5/server/api/stream/IStreamAwareScopeHandler.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.server.api.scope.IScopeHandler; - -/** - * A scope handler that is stream aware. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IStreamAwareScopeHandler extends IScopeHandler { - /** - * A broadcast stream starts being published. This will be called - * when the first video packet has been received. - * - * @param stream stream - */ - public void streamPublishStart(IBroadcastStream stream); - - /** - * A broadcast stream starts being recorded. This will be called - * when the first video packet has been received. - * - * @param stream stream - */ - public void streamRecordStart(IBroadcastStream stream); - - /** - * A broadcast stream stops being recorded. This will be called - * when the record-stop notification is sent to the Flash client. - * - * @param stream stream - */ - public void streamRecordStop(IBroadcastStream stream); - - /** - * Notified when a broadcaster starts. - * - */ - public void streamBroadcastStart(IBroadcastStream stream); - - /** - * Notified when a broadcaster closes. - * - * @param stream stream - */ - public void streamBroadcastClose(IBroadcastStream stream); - - /** - * Notified when a subscriber starts. - * - * @param stream stream - */ - public void streamSubscriberStart(ISubscriberStream stream); - - /** - * Notified when a subscriber closes. - * - * @param stream stream - */ - public void streamSubscriberClose(ISubscriberStream stream); - - /** - * Notified when a play item plays. - * - * @param stream stream - * @param item item - * @param isLive true if live - */ - public void streamPlayItemPlay(ISubscriberStream stream, IPlayItem item, boolean isLive); - - /** - * Notified when a play item stops. - * - * @param stream stream - * @param item item - */ - public void streamPlayItemStop(ISubscriberStream stream, IPlayItem item); - - /** - * Notified when a play item pauses. - * - * @param stream stream - * @param item item - * @param position position - */ - public void streamPlayItemPause(ISubscriberStream stream, IPlayItem item, int position); - - /** - * Notified when a play item resumes. - * - * @param stream stream - * @param item item - * @param position position - */ - public void streamPlayItemResume(ISubscriberStream stream, IPlayItem item, int position); - - /** - * Notified when a play item seeks. - * - * @param stream stream - * @param item item - * @param position position - */ - public void streamPlayItemSeek(ISubscriberStream stream, IPlayItem item, int position); - -} diff --git a/src/main/java/org/red5/server/api/stream/IStreamCapableConnection.java b/src/main/java/org/red5/server/api/stream/IStreamCapableConnection.java deleted file mode 100644 index a9a15cb69..000000000 --- a/src/main/java/org/red5/server/api/stream/IStreamCapableConnection.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.server.api.IConnection; - -/** - * A connection that supports streaming. - * - * @author The Red5 Project - * @author Luke Hubbard (luke@codegent.com) - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IStreamCapableConnection extends IConnection { - - /** - * Return a reserved stream id for use. - * According to FCS/FMS regulation, the base is 1. - * @return Reserved stream id - */ - int reserveStreamId(); - - int reserveStreamId(int id); - - /** - * Unreserve this id for future use. - * - * @param streamId ID of stream to unreserve - */ - void unreserveStreamId(int streamId); - - /** - * Deletes the stream with the given id. - * - * @param streamId ID of stream to delete - */ - void deleteStreamById(int streamId); - - /** - * Get a stream by its id. - * - * @param streamId Stream id - * @return Stream with given id - */ - IClientStream getStreamById(int streamId); - - /** - * Create a stream that can play only one item. - * - * @param streamId Stream id - * @return New subscriber stream that can play only one item - */ - ISingleItemSubscriberStream newSingleItemSubscriberStream(int streamId); - - /** - * Create a stream that can play a list. - * - * @param streamId Stream id - * @return New stream that can play sequence of items - */ - IPlaylistSubscriberStream newPlaylistSubscriberStream(int streamId); - - /** - * Create a broadcast stream. - * - * @param streamId Stream id - * @return New broadcast stream - */ - IClientBroadcastStream newBroadcastStream(int streamId); - - /** - * Total number of video messages that are pending to be sent to a stream. - * - * @param streamId Stream id - * @return Number of pending video messages - */ - long getPendingVideoMessages(int streamId); - -} diff --git a/src/main/java/org/red5/server/api/stream/IStreamFilenameGenerator.java b/src/main/java/org/red5/server/api/stream/IStreamFilenameGenerator.java deleted file mode 100644 index fc2179555..000000000 --- a/src/main/java/org/red5/server/api/stream/IStreamFilenameGenerator.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeService; - -/** - * A class that can generate filenames for streams. - * - * @author The Red5 Project - * @author Joachim Bauch (bauch@struktur.de) - */ -public interface IStreamFilenameGenerator extends IScopeService { - - /** Name of the bean to setup a custom filename generator in an application. */ - public static String BEAN_NAME = "streamFilenameGenerator"; - - /** Possible filename generation types. */ - public static enum GenerationType { - PLAYBACK, RECORD - }; - - /** - * Generate a filename without an extension. - * - * @param scope Scope to use - * @param name Stream name - * @param type Generation strategy (either playback or record) - * @return Full filename - */ - public String generateFilename(IScope scope, String name, GenerationType type); - - /** - * Generate a filename with an extension. - * - * @param scope Scope to use - * @param name Stream filename - * @param extension Extension - * @param type Generation strategy (either playback or record) - * @return Full filename with extension - */ - public String generateFilename(IScope scope, String name, String extension, GenerationType type); - - /** - * True if returned filename is an absolute path, else relative to application. - * - * If relative to application, you need to use - * scope.getContext().getResources(fileName)[0].getFile() to resolve - * this to a file. - * - * If absolute (ie returns true) simply use new File(generateFilename(scope, name)) - * - * @return true if an absolute path; else false - */ - public boolean resolvesToAbsolutePath(); - -} diff --git a/src/main/java/org/red5/server/api/stream/IStreamListener.java b/src/main/java/org/red5/server/api/stream/IStreamListener.java deleted file mode 100644 index 35415e6d8..000000000 --- a/src/main/java/org/red5/server/api/stream/IStreamListener.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -/** - * Listener that is notified about packets received from a stream. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IStreamListener { - - /** - * A packet has been received from a stream. - * - * @param stream the stream the packet has been received for - * @param packet the packet received - */ - public void packetReceived(IBroadcastStream stream, IStreamPacket packet); - -} diff --git a/src/main/java/org/red5/server/api/stream/IStreamPacket.java b/src/main/java/org/red5/server/api/stream/IStreamPacket.java deleted file mode 100644 index 346ec2440..000000000 --- a/src/main/java/org/red5/server/api/stream/IStreamPacket.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.apache.mina.core.buffer.IoBuffer; - -/** - * Packet containing stream data. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IStreamPacket { - - /** - * Type of this packet. This is one of the TYPE_ constants. - * - * @return the type - */ - public byte getDataType(); - - /** - * Timestamp of this packet. - * - * @return the timestamp in milliseconds - */ - public int getTimestamp(); - - /** - * Packet contents. - * - * @return the contents - */ - public IoBuffer getData(); - -} diff --git a/src/main/java/org/red5/server/api/stream/IStreamPlaybackSecurity.java b/src/main/java/org/red5/server/api/stream/IStreamPlaybackSecurity.java deleted file mode 100644 index 8b659d878..000000000 --- a/src/main/java/org/red5/server/api/stream/IStreamPlaybackSecurity.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.server.api.scope.IScope; - -/** - * Interface for handlers that control access to stream playback. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IStreamPlaybackSecurity { - - /** - * Check if playback of a stream with the given name is allowed. - * - * @param scope Scope the stream is about to be played back from. - * @param name Name of the stream to play. - * @param start Position to start playback from (in milliseconds). - * @param length Duration to play (in milliseconds). - * @param flushPlaylist Flush playlist? - * @return True if playback is allowed, otherwise False - */ - public boolean isPlaybackAllowed(IScope scope, String name, int start, int length, boolean flushPlaylist); - -} diff --git a/src/main/java/org/red5/server/api/stream/IStreamPublishSecurity.java b/src/main/java/org/red5/server/api/stream/IStreamPublishSecurity.java deleted file mode 100644 index 566db7096..000000000 --- a/src/main/java/org/red5/server/api/stream/IStreamPublishSecurity.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.server.api.scope.IScope; - -/** - * Interface for handlers that control access to stream publishing. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IStreamPublishSecurity { - - /** - * Check if publishing a stream with the given name is allowed. - * - * @param scope Scope the stream is about to be published in. - * @param name Name of the stream to publish. - * @param mode Publishing mode. - * @return True if publishing is allowed, otherwise False - */ - public boolean isPublishAllowed(IScope scope, String name, String mode); - -} diff --git a/src/main/java/org/red5/server/api/stream/IStreamService.java b/src/main/java/org/red5/server/api/stream/IStreamService.java deleted file mode 100644 index 2d5c075b3..000000000 --- a/src/main/java/org/red5/server/api/stream/IStreamService.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import org.red5.server.api.IConnection; -import org.red5.server.api.scope.IScopeService; - -/** - * This interface represents the stream methods that can be called throug RTMP. - */ -public interface IStreamService extends IScopeService { - - public static String BEAN_NAME = "streamService"; - - /** - * Create a stream and return a corresponding id. - * - * @return ID of created stream - */ - public int createStream(); - - /** - * Close the stream but not deallocate the resources. - * - * @param connection Connection - * @param streamId Stream id - */ - public void closeStream(IConnection connection, int streamId); - - /** - * Close the stream if not been closed. - * Deallocate the related resources. - * @param streamId Stream id - */ - public void deleteStream(int streamId); - - /** - * Called by FMS. - * - * @param streamId Stream id - */ - public void initStream(int streamId); - - /** - * Called by FME. - * - * @param streamName stream name - */ - public void releaseStream(String streamName); - - /** - * Delete stream - * @param conn Stream capable connection - * @param streamId Stream id - */ - public void deleteStream(IStreamCapableConnection conn, int streamId); - - /** - * Play stream without initial stop - * @param dontStop Stoppage flag - */ - public void play(Boolean dontStop); - - /** - * Play stream with name - * @param name Stream name - */ - public void play(String name); - - /** - * Play stream with name from start position - * @param name Stream name - * @param start Start position - */ - public void play(String name, int start); - - /** - * Play stream with name from start position and for given amount if time - * @param name Stream name - * @param start Start position - * @param length Playback length - */ - public void play(String name, int start, int length); - - /** - * Publishes stream from given position for given amount of time - * @param name Stream published name - * @param start Start position - * @param length Playback length - * @param flushPlaylist Flush playlist? - */ - public void play(String name, int start, int length, boolean flushPlaylist); - - /** - * Publishes stream with given name - * @param name Stream published name - */ - public void publish(String name); - - /** - * Publishes stream with given name and mode - * @param name Stream published name - * @param mode Stream publishing mode - */ - public void publish(String name, String mode); - - /** - * Publish - * @param dontStop Whether need to stop first - */ - public void publish(Boolean dontStop); - - /** - * Seek to position - * @param position Seek position - */ - public void seek(int position); - - /** - * Pauses playback - * @param pausePlayback Pause or resume flash - * @param position Pause position - */ - public void pause(Boolean pausePlayback, int position); - - /** - * Undocumented Flash Plugin 10 call, assuming to be the alias to pause(boolean, int) - * - * @param pausePlayback Pause or resume flash - * @param position Pause position - */ - public void pauseRaw(Boolean pausePlayback, int position); - - /** - * Can recieve video? - * @param receive Boolean flag - */ - public void receiveVideo(boolean receive); - - /** - * Can recieve audio? - * @param receive Boolean flag - */ - public void receiveAudio(boolean receive); - -} diff --git a/src/main/java/org/red5/server/api/stream/IStreamableFileFactory.java b/src/main/java/org/red5/server/api/stream/IStreamableFileFactory.java deleted file mode 100644 index 002fc114f..000000000 --- a/src/main/java/org/red5/server/api/stream/IStreamableFileFactory.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import java.io.File; -import java.util.Set; - -import org.red5.server.api.scope.IScopeService; -import org.red5.server.api.service.IStreamableFileService; - -/** - * Scope service extension that provides method to get streamable file services set - */ -public interface IStreamableFileFactory extends IScopeService { - - public static String BEAN_NAME = "streamableFileFactory"; - - public abstract IStreamableFileService getService(File fp); - - /** - * Getter for services - * - * @return Set of streamable file services - */ - public abstract Set getServices(); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/api/stream/ISubscriberStream.java b/src/main/java/org/red5/server/api/stream/ISubscriberStream.java deleted file mode 100644 index 764ca7275..000000000 --- a/src/main/java/org/red5/server/api/stream/ISubscriberStream.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -import java.io.IOException; - -import org.red5.server.api.scheduling.IScheduledJob; - -/** - * ISubscriberStream is a stream from subscriber's point of view. That is, it - * provides methods for common stream operations like play, pause or seek. - */ -public interface ISubscriberStream extends IClientStream { - /** - * Start playing. - * - * @throws IOException if an IO error occurred while starting to play the stream - */ - void play() throws IOException; - - /** - * Pause at a position for current playing item. - * - * @param position - * Position for pause in millisecond. - */ - void pause(int position); - - /** - * Resume from a position for current playing item. - * - * @param position - * Position for resume in millisecond. - */ - void resume(int position); - - /** - * Stop playing. - */ - void stop(); - - /** - * Seek into a position for current playing item. - * - * @param position - * Position for seek in millisecond. - * @throws OperationNotSupportedException if the stream doesn't support seeking. - */ - void seek(int position) throws OperationNotSupportedException; - - /** - * Check if the stream is currently paused. - * - * @return stream is paused - */ - boolean isPaused(); - - /** - * Should the stream send video to the client? - * - * @param receive - */ - void receiveVideo(boolean receive); - - /** - * Should the stream send audio to the client? - * - * @param receive - */ - void receiveAudio(boolean receive); - - /** - * Return the streams state enum. - * - * @return current state - */ - public StreamState getState(); - - /** - * Sets the streams state enum. - * - * @param state sets current state - */ - public void setState(StreamState state); - - /** - * Notification of state change and associated parameters. - * - * @param state new state - * @param changed parameters associated with the change - */ - public void onChange(final StreamState state, final Object... changed); - - /** - * Schedule a job to be executed only once after a 10ms delay. - * - * @param job - * @return jobName - */ - public String scheduleOnceJob(IScheduledJob job); - - /** - * Schedule a job to be executed regularly at the given interval. - * - * @param job - * @param interval - * @return jobName - */ - public String scheduleWithFixedDelay(IScheduledJob job, int interval); - - /** - * Cancels a scheduled job by name. - * - * @param pullAndPush - */ - public void cancelJob(String pullAndPush); - -} diff --git a/src/main/java/org/red5/server/api/stream/OperationNotSupportedException.java b/src/main/java/org/red5/server/api/stream/OperationNotSupportedException.java deleted file mode 100644 index 05fb789c0..000000000 --- a/src/main/java/org/red5/server/api/stream/OperationNotSupportedException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -/** - * The requested operation is not supported by the stream. - * - */ -public class OperationNotSupportedException extends Exception { - - /** - * - */ - private static final long serialVersionUID = -6166602683688991431L; - -} diff --git a/src/main/java/org/red5/server/api/stream/ResourceExistException.java b/src/main/java/org/red5/server/api/stream/ResourceExistException.java deleted file mode 100644 index b688fa6db..000000000 --- a/src/main/java/org/red5/server/api/stream/ResourceExistException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -public class ResourceExistException extends Exception { - private static final long serialVersionUID = 443389396219999143L; - - /** Constructs a new ResourceExistException. */ - public ResourceExistException() { - super(); - } - - public ResourceExistException(String message, Throwable cause) { - super(message, cause); - } - - public ResourceExistException(String message) { - super(message); - } - - public ResourceExistException(Throwable cause) { - super(cause); - } - -} diff --git a/src/main/java/org/red5/server/api/stream/ResourceNotFoundException.java b/src/main/java/org/red5/server/api/stream/ResourceNotFoundException.java deleted file mode 100644 index 0a1cc5f60..000000000 --- a/src/main/java/org/red5/server/api/stream/ResourceNotFoundException.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -/** - * @author daccattato - * - */ -public class ResourceNotFoundException extends Exception { - private static final long serialVersionUID = -1963629259187714996L; - - /** Constructs a new ResourceNotFoundException. */ - public ResourceNotFoundException() { - super(); - } - - public ResourceNotFoundException(String message, Throwable cause) { - super(message, cause); - } - - public ResourceNotFoundException(String message) { - super(message); - } - - public ResourceNotFoundException(Throwable cause) { - super(cause); - } - -} diff --git a/src/main/java/org/red5/server/api/stream/StreamState.java b/src/main/java/org/red5/server/api/stream/StreamState.java deleted file mode 100644 index a934df373..000000000 --- a/src/main/java/org/red5/server/api/stream/StreamState.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream; - -/** - * Represents all the states that a stream may be in at a requested point in time. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public enum StreamState { - - INIT, UNINIT, OPEN, CLOSED, STARTED, STOPPED, PLAYING, PAUSED, RESUMED, END, SEEK; - -} diff --git a/src/main/java/org/red5/server/api/stream/support/DynamicPlayItem.java b/src/main/java/org/red5/server/api/stream/support/DynamicPlayItem.java deleted file mode 100644 index 1d943d0f6..000000000 --- a/src/main/java/org/red5/server/api/stream/support/DynamicPlayItem.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream.support; - -import org.red5.server.api.stream.IPlayItem; -import org.red5.server.messaging.IMessageInput; - -/** - * Dynamic playlist item implementation - */ -public class DynamicPlayItem implements IPlayItem { - - /** - * Playlist item name - */ - protected final String name; - - /** - * Start mark - */ - protected final long start; - - /** - * Length - amount to play - */ - protected final long length; - - /** - * Size - for VOD items this will be the file size - */ - protected long size = -1; - - /** - * Offset - */ - protected double offset; - - /** - * Message source - */ - protected IMessageInput msgInput; - - private DynamicPlayItem(String name, long start, long length) { - this.name = name; - this.start = start; - this.length = length; - } - - private DynamicPlayItem(String name, long start, long length, double offset) { - this.name = name; - this.start = start; - this.length = length; - this.offset = offset; - } - - /** - * Returns play item length in milliseconds - * - * @return Play item length in milliseconds - */ - public long getLength() { - return length; - } - - /** - * Returns IMessageInput object. IMessageInput is an endpoint for a consumer - * to connect. - * - * @return IMessageInput object - */ - public IMessageInput getMessageInput() { - return msgInput; - } - - /** - * Returns item name - * - * @return item name - */ - public String getName() { - return name; - } - - /** - * Returns boolean value that specifies whether item can be played - */ - public long getStart() { - return start; - } - - /** - * Alias for getMessageInput - * - * @return Message input source - */ - public IMessageInput getMsgInput() { - return msgInput; - } - - /** - * Setter for message input - * - * @param msgInput Message input - */ - public void setMsgInput(IMessageInput msgInput) { - this.msgInput = msgInput; - } - - /** - * Returns size in bytes - */ - public long getSize() { - return size; - } - - /** - * Set the size in bytes - * - * @param size size in bytes - */ - public void setSize(long size) { - this.size = size; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + (int) (size ^ (size >>> 32)); - result = prime * result + (int) (start ^ (start >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DynamicPlayItem other = (DynamicPlayItem) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (size != other.size) - return false; - if (start != other.start) - return false; - return true; - } - - /** - * Builder for DynamicPlayItem - * - * @param name - * @param start - * @param length - * @return play item instance - */ - public static DynamicPlayItem build(String name, long start, long length) { - DynamicPlayItem playItem = new DynamicPlayItem(name, start, length); - return playItem; - } - - /** - * Builder for DynamicPlayItem - * - * @param name - * @param start - * @param length - * @param offset - * @return play item instance - */ - public static DynamicPlayItem build(String name, long start, long length, double offset) { - DynamicPlayItem playItem = new DynamicPlayItem(name, start, length, offset); - return playItem; - } - -} diff --git a/src/main/java/org/red5/server/api/stream/support/SimplePlayItem.java b/src/main/java/org/red5/server/api/stream/support/SimplePlayItem.java deleted file mode 100644 index f1760872f..000000000 --- a/src/main/java/org/red5/server/api/stream/support/SimplePlayItem.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.api.stream.support; - -import org.red5.server.api.stream.IPlayItem; -import org.red5.server.messaging.IMessageInput; - -/** - * Simple playlist item implementation - */ -public class SimplePlayItem implements IPlayItem, Comparable { - - private long created = System.nanoTime(); - - /** - * Playlist item name - */ - protected final String name; - - /** - * Start mark - */ - protected final long start; - - /** - * Length - amount to play - */ - protected final long length; - - /** - * Message source - */ - protected IMessageInput msgInput; - - private SimplePlayItem(String name) { - this.name = name; - this.start = -2L; - this.length = -1L; - } - - private SimplePlayItem(String name, long start, long length) { - this.name = name; - this.start = start; - this.length = length; - } - - /** - * Returns play item length in milliseconds - * - * @return Play item length in milliseconds - */ - public long getLength() { - return length; - } - - /** - * Returns IMessageInput object. IMessageInput is an endpoint for a consumer - * to connect. - * - * @return IMessageInput object - */ - public IMessageInput getMessageInput() { - return msgInput; - } - - /** - * Returns item name - * - * @return item name - */ - public String getName() { - return name; - } - - /** - * Returns boolean value that specifies whether item can be played - */ - public long getStart() { - return start; - } - - /** - * Alias for getMessageInput - * - * @return Message input source - */ - public IMessageInput getMsgInput() { - return msgInput; - } - - /** - * Setter for message input - * - * @param msgInput Message input - */ - public void setMsgInput(IMessageInput msgInput) { - this.msgInput = msgInput; - } - - /** - * @return the created - */ - public long getCreated() { - return created; - } - - /** - * @param created the created to set - */ - public void setCreated(long created) { - this.created = created; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + (int) (start ^ (start >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SimplePlayItem other = (SimplePlayItem) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (start != other.start) - return false; - return true; - } - - @Override - public int compareTo(SimplePlayItem that) { - if (created > that.getCreated()) { - return -1; - } else if (created < that.getCreated()) { - return 1; - } - return 0; - } - - /** - * Builder for SimplePlayItem - * - * @param name - * @return play item instance - */ - public static SimplePlayItem build(String name) { - SimplePlayItem playItem = new SimplePlayItem(name); - return playItem; - } - - /** - * Builder for SimplePlayItem - * - * @param name - * @param start - * @param length - * @return play item instance - */ - public static SimplePlayItem build(String name, long start, long length) { - SimplePlayItem playItem = new SimplePlayItem(name, start, length); - return playItem; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "SimplePlayItem [created=" + created + ", name=" + name + ", start=" + start + ", length=" + length + "]"; - } - -} diff --git a/src/main/java/org/red5/server/exception/ClientDetailsException.java b/src/main/java/org/red5/server/exception/ClientDetailsException.java deleted file mode 100644 index 5c2dcf981..000000000 --- a/src/main/java/org/red5/server/exception/ClientDetailsException.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.exception; - -/** - * Exception class than contains additional parameters to return to the client. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public class ClientDetailsException extends RuntimeException { - - private static final long serialVersionUID = -1908769505547253205L; - - /** - * Parameters to return to the client. - */ - private Object parameters; - - /** - * Also return stacktrace to client? - */ - private boolean stacktrace; - - /** - * Create new exception object from message and parameters. By default, no - * stacktrace is returned to the client. - * - * @param message message - * @param params parameters for message - */ - public ClientDetailsException(String message, Object params) { - this(message, params, false); - } - - /** - * Create new exception object from message and parameters with optional stacktrace. - * - * @param message message - * @param params parameters - * @param includeStacktrace whether or not to include a stack trace - */ - public ClientDetailsException(String message, Object params, boolean includeStacktrace) { - super(message); - this.parameters = params; - this.stacktrace = includeStacktrace; - } - - /** - * Get parameters to return to the client. - * - * @return parameters - */ - public Object getParameters() { - return parameters; - } - - /** - * Should the stacktrace returned to the client? - * - * @return stacktrace - */ - public boolean includeStacktrace() { - return stacktrace; - } - -} diff --git a/src/main/java/org/red5/server/exception/ClientNotFoundException.java b/src/main/java/org/red5/server/exception/ClientNotFoundException.java deleted file mode 100644 index 674ff9cfa..000000000 --- a/src/main/java/org/red5/server/exception/ClientNotFoundException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.exception; - -/** - * Client not found - */ -public class ClientNotFoundException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = 3135070223941800751L; - - /** - * Create exception from given string message - * @param id id - */ - public ClientNotFoundException(String id) { - super("Client \"" + id + "\" not found."); - } - -} diff --git a/src/main/java/org/red5/server/exception/ClientRejectedException.java b/src/main/java/org/red5/server/exception/ClientRejectedException.java deleted file mode 100644 index c8f39b168..000000000 --- a/src/main/java/org/red5/server/exception/ClientRejectedException.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.exception; - -/** - * The client is not allowed to connect. Reason that provided with this - * exception is sent to client-side status event description. - * - */ -public class ClientRejectedException extends RuntimeException { - - private static final long serialVersionUID = 9204597649465357898L; - - @SuppressWarnings("all") - private Object reason; - - /** Constructs a new ClientRejectedException. */ - public ClientRejectedException() { - this("Client rejected"); - } - - /** - * Create new exception with given rejection reason - * @param reason Rejection reason - */ - public ClientRejectedException(Object reason) { - super("Client rejected"); - this.reason = reason; - } - - /** - * Getter for reason - * - * @return Rejection reason - */ - public Object getReason() { - return reason; - } - -} diff --git a/src/main/java/org/red5/server/exception/ScopeException.java b/src/main/java/org/red5/server/exception/ScopeException.java deleted file mode 100644 index af86b2101..000000000 --- a/src/main/java/org/red5/server/exception/ScopeException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.exception; - -/** - * Generic Scope exception. - */ -public class ScopeException extends RuntimeException { - - private static final long serialVersionUID = -8512088658139011L; - - public ScopeException(String string) { - super(string); - } - -} diff --git a/src/main/java/org/red5/server/exception/ScopeNotFoundException.java b/src/main/java/org/red5/server/exception/ScopeNotFoundException.java deleted file mode 100644 index cc0e7cd55..000000000 --- a/src/main/java/org/red5/server/exception/ScopeNotFoundException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.exception; - -import org.red5.server.api.scope.IScope; - -/** - * Scope not found, thrown when child scope wasn't found. - */ -public class ScopeNotFoundException extends RuntimeException { - - private static final long serialVersionUID = -8512088658139018041L; - - /** - * Create exception from given scope object and given child subscope - * @param scope Scope - * @param childName Subscope name - */ - public ScopeNotFoundException(IScope scope, String childName) { - super("Scope not found: " + childName + " in " + scope); - } - -} diff --git a/src/main/java/org/red5/server/exception/ScopeShuttingDownException.java b/src/main/java/org/red5/server/exception/ScopeShuttingDownException.java deleted file mode 100644 index 29d7fca5e..000000000 --- a/src/main/java/org/red5/server/exception/ScopeShuttingDownException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.exception; - -import org.red5.server.api.scope.IScope; - -/** - * Scope is currently shutting down. - */ -public class ScopeShuttingDownException extends RuntimeException { - - private static final long serialVersionUID = 9129189610425512289L; - - /** - * Create exception from given scope object - * @param scope Scope - */ - public ScopeShuttingDownException(IScope scope) { - super("Scope shutting down: " + scope); - } - -} diff --git a/src/main/java/org/red5/server/jmx/mxbeans/AttributeStoreMXBean.java b/src/main/java/org/red5/server/jmx/mxbeans/AttributeStoreMXBean.java deleted file mode 100644 index f0e4827c5..000000000 --- a/src/main/java/org/red5/server/jmx/mxbeans/AttributeStoreMXBean.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.jmx.mxbeans; - -import java.util.Set; - -import javax.management.MXBean; - -/** - * Base interface for all API objects with attributes - * - * @author The Red5 Project - * @author Paul Gregoire (mondain@gmail.com) - */ -@MXBean -public interface AttributeStoreMXBean { - - public Set getAttributeNames(); - - public boolean hasAttribute(String name); - - public boolean removeAttribute(String name); - - public void removeAttributes(); - -} diff --git a/src/main/java/org/red5/server/jmx/mxbeans/ClientBroadcastStreamMXBean.java b/src/main/java/org/red5/server/jmx/mxbeans/ClientBroadcastStreamMXBean.java deleted file mode 100644 index b132a8721..000000000 --- a/src/main/java/org/red5/server/jmx/mxbeans/ClientBroadcastStreamMXBean.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.jmx.mxbeans; - -import java.io.IOException; - -import javax.management.MXBean; - -import org.red5.server.api.stream.ResourceExistException; -import org.red5.server.api.stream.ResourceNotFoundException; - -/** - * Represents live stream broadcasted from client. As Flash Media Server, Red5 supports - * recording mode for live streams, that is, broadcasted stream has broadcast mode. It can be either - * "live" or "record" and latter causes server-side application to record broadcasted stream. - * - * Note that recorded streams are recorded as FLV files. The same is correct for audio, because - * NellyMoser codec that Flash Player uses prohibits on-the-fly transcoding to audio formats like MP3 - * without paying of licensing fee or buying SDK. - * - * This type of stream uses two different pipes for live streaming and recording. - */ -@MXBean -public interface ClientBroadcastStreamMXBean { - - public void start(); - - public void startPublishing(); - - public void stop(); - - public void close(); - - public void saveAs(String name, boolean isAppend) throws IOException, ResourceNotFoundException, ResourceExistException; - - public String getSaveFilename(); - - public String getPublishedName(); - - public void setPublishedName(String name); - -} diff --git a/src/main/java/org/red5/server/jmx/mxbeans/ClientRegistryMXBean.java b/src/main/java/org/red5/server/jmx/mxbeans/ClientRegistryMXBean.java deleted file mode 100644 index 6e21ccafd..000000000 --- a/src/main/java/org/red5/server/jmx/mxbeans/ClientRegistryMXBean.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.jmx.mxbeans; - -import java.util.List; - -import javax.management.MXBean; - -import org.red5.server.Client; -import org.red5.server.exception.ClientNotFoundException; - -/** - * An MBean interface for the client registry. - * - * @author The Red5 Project - * @author Paul Gregoire (mondain@gmail.com) - */ -@MXBean -public interface ClientRegistryMXBean { - - public String nextId(); - - public String previousId(); - - public boolean hasClient(String id); - - public List getClientList(); - - public Client getClient(String id) throws ClientNotFoundException; - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/jmx/mxbeans/QuartzSchedulingServiceMXBean.java b/src/main/java/org/red5/server/jmx/mxbeans/QuartzSchedulingServiceMXBean.java deleted file mode 100644 index e6f398fa0..000000000 --- a/src/main/java/org/red5/server/jmx/mxbeans/QuartzSchedulingServiceMXBean.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.jmx.mxbeans; - -import java.util.List; - -import javax.management.MXBean; - -/** - * Scheduling service that uses Quartz as backend. - * - * @author The Red5 Project - * @author Paul Gregoire (mondain@gmail.com) - */ -@MXBean -public interface QuartzSchedulingServiceMXBean { - - /** - * Getter for job name. - * - * @return Job name - */ - public String getJobName(); - - public void removeScheduledJob(String name); - - public List getScheduledJobNames(); - -} diff --git a/src/main/java/org/red5/server/jmx/mxbeans/RTMPConnectionMXBean.java b/src/main/java/org/red5/server/jmx/mxbeans/RTMPConnectionMXBean.java deleted file mode 100644 index 18cc2e79b..000000000 --- a/src/main/java/org/red5/server/jmx/mxbeans/RTMPConnectionMXBean.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.jmx.mxbeans; - -import java.util.List; - -import javax.management.MXBean; - -@MXBean -public interface RTMPConnectionMXBean extends AttributeStoreMXBean { - - public String getType(); - - public String getHost(); - - public String getRemoteAddress(); - - public List getRemoteAddresses(); - - public int getRemotePort(); - - public String getPath(); - - public String getSessionId(); - - public boolean isConnected(); - - public void close(); - - public long getReadBytes(); - - public long getWrittenBytes(); - - public long getReadMessages(); - - public long getWrittenMessages(); - - public long getDroppedMessages(); - - public long getPendingMessages(); - - public long getPendingVideoMessages(int streamId); - -} diff --git a/src/main/java/org/red5/server/jmx/mxbeans/RTMPMinaConnectionMXBean.java b/src/main/java/org/red5/server/jmx/mxbeans/RTMPMinaConnectionMXBean.java deleted file mode 100644 index 0b5d48862..000000000 --- a/src/main/java/org/red5/server/jmx/mxbeans/RTMPMinaConnectionMXBean.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.jmx.mxbeans; - -import javax.management.MXBean; - -@MXBean -public interface RTMPMinaConnectionMXBean extends RTMPConnectionMXBean { - - public void invokeMethod(String method); - -} diff --git a/src/main/java/org/red5/server/jmx/mxbeans/RTMPMinaTransportMXBean.java b/src/main/java/org/red5/server/jmx/mxbeans/RTMPMinaTransportMXBean.java deleted file mode 100644 index 7a7df8251..000000000 --- a/src/main/java/org/red5/server/jmx/mxbeans/RTMPMinaTransportMXBean.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.jmx.mxbeans; - -import javax.management.MXBean; - -/** - * - */ -@MXBean -public interface RTMPMinaTransportMXBean { - public void setIoThreads(int ioThreads); - - public void setTcpNoDelay(boolean tcpNoDelay); - - public void setUseHeapBuffers(boolean useHeapBuffers); - - public String getAddress(); - - public String getStatistics(); - - public void start() throws Exception; - - public void stop(); -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/jmx/mxbeans/ScopeMXBean.java b/src/main/java/org/red5/server/jmx/mxbeans/ScopeMXBean.java deleted file mode 100644 index 9dec2fc03..000000000 --- a/src/main/java/org/red5/server/jmx/mxbeans/ScopeMXBean.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.jmx.mxbeans; - -import java.util.Set; - -import javax.management.MXBean; - -import org.red5.server.api.scope.ScopeType; - -/** - * An MBean interface for the scope object. - * - * @author The Red5 Project - * @author Paul Gregoire (mondain@gmail.com) - */ -@MXBean -public interface ScopeMXBean { - - /** - * Check if scope is enabled - * @return true if scope is enabled, false otherwise - */ - public boolean getEnabled(); - - /** - * Enable or disable scope by setting enable flag - * @param enabled Enable flag value - */ - public void setEnabled(boolean enabled); - - /** - * Check if scope is in running state - * @return true if scope is in running state, false otherwise - */ - public boolean getRunning(); - - /** - * Setter for autostart flag - * @param autoStart Autostart flag value - */ - public void setAutoStart(boolean autoStart); - - /** - * Initialization actions, start if autostart is set to true - */ - public void init(); - - /** - * Starts scope - * @return true if scope has handler and it's start method returned true, false otherwise - */ - public boolean start(); - - /** - * Stops scope - */ - public void stop(); - - /** - * Destroys scope - * - * @throws Exception - */ - public void destroy() throws Exception; - - /** - * Set scope persistence class - * - * @param persistenceClass Scope's persistence class - * @throws Exception Exception - */ - public void setPersistenceClass(String persistenceClass) throws Exception; - - /** - * Setter for child load path. Should be implemented in subclasses? - * @param pattern Load path pattern - */ - public void setChildLoadPath(String pattern); - - /** - * Check whether scope has child scope with given name - * @param name Child scope name - * @return true if scope has child node with given name, false otherwise - */ - public boolean hasChildScope(String name); - - /** - * Check whether scope has child scope with given name and type - * @param type Child scope type - * @param name Child scope name - * @return true if scope has child node with given name and type, false otherwise - */ - public boolean hasChildScope(ScopeType type, String name); - - /** - * Check if scope has a context - * @return true if scope has context, false otherwise - */ - public boolean hasContext(); - - /** - * Return scope context path - * @return Scope context path - */ - public String getContextPath(); - - /** - * Setter for scope name - * @param name Scope name - */ - public void setName(String name); - - /** - * Return scope path calculated from parent path and parent scope name - * @return Scope path - */ - public String getPath(); - - /** - * Check if scope or it's parent has handler - * @return true if scope or it's parent scope has a handler, false otherwise - */ - public boolean hasHandler(); - - /** - * Check if scope has parent scope - * @return true if scope has parent scope, false otherwise` - */ - public boolean hasParent(); - - /** - * Set scope depth - * @param depth Scope depth - */ - public void setDepth(int depth); - - /** - * return scope depth - * @return Scope depth - */ - public int getDepth(); - - /** - * Create child scope with given name - * @param name Child scope name - * @return true on success, false otherwise - */ - public boolean createChildScope(String name); - - /** - * Unregisters service handler by name - * @param name Service handler name - */ - public void unregisterServiceHandler(String name); - - /** - * Return set of service handler names - * @return Set of service handler names - */ - public Set getServiceHandlerNames(); - - /** - * Return total number of connections to the scope. - * - * @return number of connections - */ - public int getTotalConnections(); - - /** - * Return maximum number of concurrent connections to the scope. - * - * @return number of connections - */ - public int getMaxConnections(); - - /** - * Return current number of connections to the scope. - * - * @return number of connections - */ - public int getActiveConnections(); - - /** - * Return total number of clients connected to the scope. - * - * @return number of clients - */ - public int getTotalClients(); - - /** - * Return maximum number of clients concurrently connected to the scope. - * - * @return number of clients - */ - public int getMaxClients(); - - /** - * Return current number of clients connected to the scope. - * - * @return number of clients - */ - public int getActiveClients(); - - /** - * Return total number of subscopes created. - * - * @return number of subscopes created - */ - public int getTotalSubscopes(); - - /** - * Return maximum number of concurrently existing subscopes. - * - * @return number of subscopes - */ - public int getMaxSubscopes(); - - /** - * Return number of currently existing subscopes. - * - * @return number of subscopes - */ - public int getActiveSubscopes(); - -} diff --git a/src/main/java/org/red5/server/messaging/AbstractMessage.java b/src/main/java/org/red5/server/messaging/AbstractMessage.java deleted file mode 100644 index 29d78e97c..000000000 --- a/src/main/java/org/red5/server/messaging/AbstractMessage.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -import java.util.Map; - -/** - * Abstract base for all messages - * - * @see org.red5.server.messaging.IMessage - */ -public class AbstractMessage implements IMessage { - - protected String messageID; - - protected String correlationID; - - protected String messageType; - - protected Map extraHeaders = null; - - /** {@inheritDoc} */ - public String getMessageID() { - return messageID; - } - - /** {@inheritDoc} */ - public void setMessageID(String id) { - this.messageID = id; - } - - /** {@inheritDoc} */ - public String getCorrelationID() { - return correlationID; - } - - /** {@inheritDoc} */ - public void setCorrelationID(String id) { - this.correlationID = id; - } - - /** {@inheritDoc} */ - public String getMessageType() { - return messageType; - } - - /** {@inheritDoc} */ - public void setMessageType(String type) { - this.messageType = type; - } - - /** {@inheritDoc} */ - public boolean getBooleanProperty(String name) { - return false; - } - - /** {@inheritDoc} */ - public void setBooleanProperty(String name, boolean value) { - } - - /** {@inheritDoc} */ - public byte getByteProperty(String name) { - return 0; - } - - /** {@inheritDoc} */ - public void setByteProperty(String name, byte value) { - } - - /** {@inheritDoc} */ - public double getDoubleProperty(String name) { - return 0; - } - - /** {@inheritDoc} */ - public void setDoubleProperty(String name, double value) { - } - - /** {@inheritDoc} */ - public float getFloatProperty(String name) { - return 0; - } - - /** {@inheritDoc} */ - public void setFloatProperty(String name, float value) { - } - - /** {@inheritDoc} */ - public int getIntProperty(String name) { - return 0; - } - - /** {@inheritDoc} */ - public void setIntProperty(String name, int value) { - } - - /** {@inheritDoc} */ - public long getLongProperty(String name) { - return 0; - } - - /** {@inheritDoc} */ - public void setLongProperty(String name, long value) { - } - - /** {@inheritDoc} */ - public short getShortProperty(String name) { - return 0; - } - - /** {@inheritDoc} */ - public void setShortProperty(String name, short value) { - } - - /** {@inheritDoc} */ - public String getStringProperty(String name) { - return null; - } - - /** {@inheritDoc} */ - public void setStringProperty(String name, String value) { - } - - /** {@inheritDoc} */ - public Object getObjectProperty(String name) { - return null; - } - - /** {@inheritDoc} */ - public void setObjectProperty(String name, Object value) { - } - -} diff --git a/src/main/java/org/red5/server/messaging/AbstractPipe.java b/src/main/java/org/red5/server/messaging/AbstractPipe.java deleted file mode 100644 index c0a7a8ee9..000000000 --- a/src/main/java/org/red5/server/messaging/AbstractPipe.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.scheduling.concurrent.CustomizableThreadFactory; - -/** - * Abstract pipe that books providers/consumers and listeners. - * Aim to ease the implementation of concrete pipes. For more - * information on what pipe is, see IPipe interface documentation. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - * - * @see org.red5.server.messaging.IPipe - */ -public abstract class AbstractPipe implements IPipe { - - private static final Logger log = LoggerFactory.getLogger(AbstractPipe.class); - - /** - * Pipe consumers list - */ - protected volatile CopyOnWriteArrayList consumers = new CopyOnWriteArrayList(); - - /** - * Pipe providers list - */ - protected volatile CopyOnWriteArrayList providers = new CopyOnWriteArrayList(); - - /** - * Event listeners - */ - protected volatile CopyOnWriteArrayList listeners = new CopyOnWriteArrayList(); - - /** - * Executor service used to run pipe tasks. - */ - private static ExecutorService taskExecutor; - - /** - * Connect consumer to this pipe. Doesn't allow to connect one consumer twice. - * Does register event listeners if instance of IPipeConnectionListener is given. - * - * @param consumer Consumer - * @param paramMap Parameters passed with connection, used in concrete pipe implementations - * @return true if consumer was added, false otherwise - */ - public boolean subscribe(IConsumer consumer, Map paramMap) { - // if consumer is listener object register it as listener - if (consumer instanceof IPipeConnectionListener) { - listeners.addIfAbsent((IPipeConnectionListener) consumer); - } - // pipe is possibly used by dozens of threads at once (like many subscribers for one server stream) - return consumers.addIfAbsent(consumer); - } - - /** - * Connect provider to this pipe. Doesn't allow to connect one provider twice. - * Does register event listeners if instance of IPipeConnectionListener is given. - * - * @param provider Provider - * @param paramMap Parameters passed with connection, used in concrete pipe implementations - * @return true if provider was added, false otherwise - */ - public boolean subscribe(IProvider provider, Map paramMap) { - // register event listener if given - if (provider instanceof IPipeConnectionListener) { - listeners.add((IPipeConnectionListener) provider); - } - return providers.addIfAbsent(provider); - } - - /** - * Disconnects provider from this pipe. Fires pipe connection event. - * @param provider Provider that should be removed - * @return true on success, false otherwise - */ - public boolean unsubscribe(IProvider provider) { - if (providers.remove(provider)) { - fireProviderConnectionEvent(provider, PipeConnectionEvent.PROVIDER_DISCONNECT, null); - listeners.remove(provider); - return true; - } else { - return false; - } - } - - /** - * Disconnects consumer from this pipe. Fires pipe connection event. - * @param consumer Consumer that should be removed - * @return true on success, false otherwise - */ - public boolean unsubscribe(IConsumer consumer) { - if (consumers.remove(consumer)) { - fireConsumerConnectionEvent(consumer, PipeConnectionEvent.CONSUMER_DISCONNECT, null); - listeners.remove(consumer); - return true; - } else { - return false; - } - } - - /** - * Registers pipe connect events listener - * @param listener Listener - */ - public void addPipeConnectionListener(IPipeConnectionListener listener) { - listeners.add(listener); - } - - /** - * Removes pipe connection listener - * @param listener Listener - */ - public void removePipeConnectionListener(IPipeConnectionListener listener) { - listeners.remove(listener); - } - - /** - * Send out-of-band ("special") control message to all consumers - * - * @param provider Provider, may be used in concrete implementations - * @param oobCtrlMsg Out-of-band control message - */ - public void sendOOBControlMessage(IProvider provider, OOBControlMessage oobCtrlMsg) { - for (IConsumer consumer : consumers) { - try { - consumer.onOOBControlMessage(provider, this, oobCtrlMsg); - } catch (Throwable t) { - log.error("exception when passing OOBCM from provider to consumers", t); - } - } - } - - /** - * Send out-of-band ("special") control message to all providers - * - * @param consumer Consumer, may be used in concrete implementations - * @param oobCtrlMsg Out-of-band control message - */ - public void sendOOBControlMessage(IConsumer consumer, OOBControlMessage oobCtrlMsg) { - for (IProvider provider : providers) { - try { - provider.onOOBControlMessage(consumer, this, oobCtrlMsg); - } catch (Throwable t) { - log.error("exception when passing OOBCM from consumer to providers", t); - } - } - } - - /** - * Getter for pipe connection events listeners - * - * @return Listeners - */ - public List getListeners() { - return Collections.unmodifiableList(listeners); - } - - /** - * Setter for pipe connection events listeners - * - * @param newListeners Listeners - */ - public void setListeners(List newListeners) { - listeners.clear(); - listeners.addAll(newListeners); - } - - /** - * Getter for providers - * - * @return Providers list - */ - public List getProviders() { - return Collections.unmodifiableList(providers); - } - - /** - * Getter for consumers - * - * @return consumers list - */ - public List getConsumers() { - return Collections.unmodifiableList(consumers); - } - - /** - * Broadcast consumer connection event - * - * @param consumer Consumer that has connected - * @param type Event type - * @param paramMap Parameters passed with connection - */ - protected void fireConsumerConnectionEvent(IConsumer consumer, int type, Map paramMap) { - // Create event object - PipeConnectionEvent event = new PipeConnectionEvent(this); - // Fill it up - event.setConsumer(consumer); - event.setType(type); - event.setParamMap(paramMap); - // Fire it - firePipeConnectionEvent(event); - } - - /** - * Broadcast provider connection event - * @param provider Provider that has connected - * @param type Event type - * @param paramMap Parameters passed with connection - */ - protected void fireProviderConnectionEvent(IProvider provider, int type, Map paramMap) { - PipeConnectionEvent event = new PipeConnectionEvent(this); - event.setProvider(provider); - event.setType(type); - event.setParamMap(paramMap); - firePipeConnectionEvent(event); - } - - /** - * Fire any pipe connection event and run all it's tasks - * @param event Pipe connection event - */ - protected void firePipeConnectionEvent(PipeConnectionEvent event) { - for (IPipeConnectionListener element : listeners) { - try { - element.onPipeConnectionEvent(event); - } catch (Throwable t) { - log.error("Exception when handling pipe connection event", t); - } - } - if (taskExecutor == null) { - taskExecutor = Executors.newCachedThreadPool(new CustomizableThreadFactory("Pipe-")); - } - // Run all of event's tasks - for (Runnable task : event.getTaskList()) { - try { - taskExecutor.execute(task); - } catch (Throwable t) { - log.warn("Exception executing pipe task {}", t); - } - } - // Clear event's tasks list - event.getTaskList().clear(); - } - - /** - * Close the pipe - */ - public void close() { - //clean up collections - if (consumers != null) { - consumers.clear(); - consumers = null; - } - if (providers != null) { - providers.clear(); - providers = null; - } - if (listeners != null) { - listeners.clear(); - listeners = null; - } - } - -} diff --git a/src/main/java/org/red5/server/messaging/IConsumer.java b/src/main/java/org/red5/server/messaging/IConsumer.java deleted file mode 100644 index 1e8fef3c9..000000000 --- a/src/main/java/org/red5/server/messaging/IConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -/** - * Signature for the message consumer. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IConsumer extends IMessageComponent { - -} diff --git a/src/main/java/org/red5/server/messaging/IFilter.java b/src/main/java/org/red5/server/messaging/IFilter.java deleted file mode 100644 index c6a4591b2..000000000 --- a/src/main/java/org/red5/server/messaging/IFilter.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -/** - * Filter marker interface groups consumer and provider interfaces - */ -public interface IFilter extends IConsumer, IProvider { - -} diff --git a/src/main/java/org/red5/server/messaging/IMessage.java b/src/main/java/org/red5/server/messaging/IMessage.java deleted file mode 100644 index 95622b8b6..000000000 --- a/src/main/java/org/red5/server/messaging/IMessage.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -/** - * Common interface for all messages. - *

Structure of messages is designed according to - * JMS Message interface. Message is composed of header and body. - * Header contains commonly used pre-defined headers - * and extensible headers.

- * - *

Each message has correlation ID that is never used so far and is subject to be removed in future.

- * - *

Message has type and number of properties.

- * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IMessage { - /** - * Return message id - * @return Message id - */ - String getMessageID(); - - /** - * Setter for new message id - * @param id Message id - */ - void setMessageID(String id); - - /** - * Return correlation id - * @return Correlation id - */ - String getCorrelationID(); - - /** - * Setter for correlation id - * @param id Correlation id - */ - void setCorrelationID(String id); - - /** - * Return message type - * @return Message type - */ - String getMessageType(); - - /** - * Setter for message type - * @param type Message type - */ - void setMessageType(String type); - - /** - * Getter for boolean property - * @param name Boolean property name - * @return Boolean property - */ - boolean getBooleanProperty(String name); - - /** - * Add boolean property to message - * @param name Boolean property name - * @param value Boolean property value - */ - void setBooleanProperty(String name, boolean value); - - /** - * Add byte property to message - * @param name Byte property name - * @return Byte property value - */ - byte getByteProperty(String name); - - /** - * Add byte property to message - * @param name Byte property name - * @param value Byte property value - */ - void setByteProperty(String name, byte value); - - /** - * Return double property by name - * @param name Double property name - * @return Double property value - */ - double getDoubleProperty(String name); - - /** - * Add double property to message - * @param name Double property name - * @param value Double property value - */ - void setDoubleProperty(String name, double value); - - /** - * Return float property by name - * @param name Float property name - * @return Float property value - */ - float getFloatProperty(String name); - - /** - * Add float property to message - * @param name Float property name - * @param value Float property value - */ - void setFloatProperty(String name, float value); - - /** - * Return int property by name - * @param name Int property name - * @return Int property value - */ - int getIntProperty(String name); - - /** - * Add int property to message - * @param name Int property name - * @param value Int property value - */ - void setIntProperty(String name, int value); - - /** - * Return long property to message - * @param name Long property name - * @return Long property value - */ - long getLongProperty(String name); - - /** - * Add long property to message - * @param name Long property name - * @param value Long property value - */ - void setLongProperty(String name, long value); - - /** - * Return short property to message - * @param name Short property name - * @return Short property value - */ - short getShortProperty(String name); - - /** - * Add short property to message - * @param name Short property name - * @param value Short property value - */ - void setShortProperty(String name, short value); - - /** - * Return string property to message - * @param name String property name - * @return String property value - */ - String getStringProperty(String name); - - /** - * Add string property to message - * @param name String property name - * @param value String property value - */ - void setStringProperty(String name, String value); - - /** - * Return object property to message - * @param name Object property name - * @return Object property value - */ - Object getObjectProperty(String name); - - /** - * Add object property to message - * @param name Object property name - * @param value Object property value - */ - void setObjectProperty(String name, Object value); -} diff --git a/src/main/java/org/red5/server/messaging/IMessageComponent.java b/src/main/java/org/red5/server/messaging/IMessageComponent.java deleted file mode 100644 index 797c78334..000000000 --- a/src/main/java/org/red5/server/messaging/IMessageComponent.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -/** - * Message component handles out-of-band control messages - */ -public interface IMessageComponent { - /** - * - * @param source Message component source - * @param pipe Connection pipe - * @param oobCtrlMsg Out-of-band control message - */ - void onOOBControlMessage(IMessageComponent source, IPipe pipe, OOBControlMessage oobCtrlMsg); -} diff --git a/src/main/java/org/red5/server/messaging/IMessageInput.java b/src/main/java/org/red5/server/messaging/IMessageInput.java deleted file mode 100644 index 01477f8c1..000000000 --- a/src/main/java/org/red5/server/messaging/IMessageInput.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -/** - * Input Endpoint for a consumer to connect. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IMessageInput { - /** - * Pull message from this input endpoint. Return - * w/o waiting. - * @return The pulled message or null if message is - * not available. - * @throws IOException on error - */ - IMessage pullMessage() throws IOException; - - /** - * Pull message from this input endpoint. Wait - * wait milliseconds if message is not available. - * @param wait milliseconds to wait when message is not - * available. - * @return The pulled message or null if message is - * not available. - */ - IMessage pullMessage(long wait); - - /** - * Connect to a consumer. - * - * @param consumer Consumer - * @param paramMap Parameters map - * @return true when successfully subscribed, - * false otherwise. - */ - boolean subscribe(IConsumer consumer, Map paramMap); - - /** - * Disconnect from a consumer. - * - * @param consumer Consumer to disconnect - * @return true when successfully unsubscribed, - * false otherwise. - */ - boolean unsubscribe(IConsumer consumer); - - /** - * Getter for consumers list. - * - * @return Consumers. - */ - List getConsumers(); - - /** - * Send OOB Control Message to all providers on the other side of pipe. - * - * @param consumer - * The consumer that sends the message - * @param oobCtrlMsg - * Out-of-band control message - */ - void sendOOBControlMessage(IConsumer consumer, OOBControlMessage oobCtrlMsg); -} diff --git a/src/main/java/org/red5/server/messaging/IMessageOutput.java b/src/main/java/org/red5/server/messaging/IMessageOutput.java deleted file mode 100644 index df3e38b91..000000000 --- a/src/main/java/org/red5/server/messaging/IMessageOutput.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -/** - * Output Endpoint for a provider to connect. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IMessageOutput { - /** - * Push a message to this output endpoint. May block - * the pusher when output can't handle the message at - * the time. - * @param message Message to be pushed. - * @throws IOException If message could not be written. - */ - void pushMessage(IMessage message) throws IOException; - - /** - * Connect to a provider. Note that params passed has nothing to deal with - * NetConnection.connect in client-side Flex/Flash RIA. - * - * @param provider Provider - * @param paramMap Parameters passed with connection - * @return true when successfully subscribed, - * false otherwise. - */ - boolean subscribe(IProvider provider, Map paramMap); - - /** - * Disconnect from a provider. - * - * @param provider Provider - * @return true when successfully unsubscribed, - * false otherwise. - */ - boolean unsubscribe(IProvider provider); - - /** - * Getter for providers - * - * @return Providers - */ - List getProviders(); - - /** - * Send OOB Control Message to all consumers on the other side of pipe. - * - * @param provider - * The provider that sends the message - * @param oobCtrlMsg - * Out-of-band control message - */ - void sendOOBControlMessage(IProvider provider, OOBControlMessage oobCtrlMsg); -} diff --git a/src/main/java/org/red5/server/messaging/IPassive.java b/src/main/java/org/red5/server/messaging/IPassive.java deleted file mode 100644 index dbf1ee6df..000000000 --- a/src/main/java/org/red5/server/messaging/IPassive.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -/** - * Signature to mark a provider/consumer never actively providers/consumers - * messages. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IPassive { - public static final String KEY = IPassive.class.getName(); -} diff --git a/src/main/java/org/red5/server/messaging/IPipe.java b/src/main/java/org/red5/server/messaging/IPipe.java deleted file mode 100644 index 8293bc588..000000000 --- a/src/main/java/org/red5/server/messaging/IPipe.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -/** - * A pipe is an object that connects message providers and - * message consumers. Its main function is to transport messages - * in kind of ways it provides. - * - * Pipes fire events as they go, these events are common way to work with pipes for - * higher level parts of server. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IPipe extends IMessageInput, IMessageOutput { - /** - * Add connection event listener to pipe - * @param listener Connection event listener - */ - void addPipeConnectionListener(IPipeConnectionListener listener); - - /** - * Add connection event listener to pipe - * @param listener Connection event listener - */ - void removePipeConnectionListener(IPipeConnectionListener listener); -} diff --git a/src/main/java/org/red5/server/messaging/IPipeConnectionListener.java b/src/main/java/org/red5/server/messaging/IPipeConnectionListener.java deleted file mode 100644 index 75d53ba23..000000000 --- a/src/main/java/org/red5/server/messaging/IPipeConnectionListener.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -/** - * A listener that wants to listen to events when - * provider/consumer connects to or disconnects from - * a specific pipe. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IPipeConnectionListener { - /** - * Pipe connection event handler - * @param event Pipe connection event - */ - void onPipeConnectionEvent(PipeConnectionEvent event); -} diff --git a/src/main/java/org/red5/server/messaging/IProvider.java b/src/main/java/org/red5/server/messaging/IProvider.java deleted file mode 100644 index 4646b7e95..000000000 --- a/src/main/java/org/red5/server/messaging/IProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -/** - * Signature for message provider. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IProvider extends IMessageComponent { - -} diff --git a/src/main/java/org/red5/server/messaging/IPushableConsumer.java b/src/main/java/org/red5/server/messaging/IPushableConsumer.java deleted file mode 100644 index 15a2b63db..000000000 --- a/src/main/java/org/red5/server/messaging/IPushableConsumer.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -import java.io.IOException; - -/** - * A consumer that supports event-driven message handling and message pushing through pipes. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public interface IPushableConsumer extends IConsumer { - public static final String KEY = IPushableConsumer.class.getName(); - - /** - * Pushes message through pipe - * - * @param pipe Pipe - * @param message Message - * @throws IOException if message could not be written - */ - void pushMessage(IPipe pipe, IMessage message) throws IOException; -} diff --git a/src/main/java/org/red5/server/messaging/InMemoryPushPushPipe.java b/src/main/java/org/red5/server/messaging/InMemoryPushPushPipe.java deleted file mode 100644 index 44fc7da73..000000000 --- a/src/main/java/org/red5/server/messaging/InMemoryPushPushPipe.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -import java.io.IOException; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A simple in-memory version of push-push pipe. It is triggered by an active provider to push messages - * through it to an event-driven consumer. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public class InMemoryPushPushPipe extends AbstractPipe { - - private static final Logger log = LoggerFactory.getLogger(InMemoryPushPushPipe.class); - - /** {@inheritDoc} */ - @Override - public boolean subscribe(IConsumer consumer, Map paramMap) { - if (consumer instanceof IPushableConsumer) { - boolean success = super.subscribe(consumer, paramMap); - if (success) { - fireConsumerConnectionEvent(consumer, PipeConnectionEvent.CONSUMER_CONNECT_PUSH, paramMap); - } - return success; - } else { - throw new IllegalArgumentException("Non-pushable consumer not supported by PushPushPipe"); - } - } - - /** {@inheritDoc} */ - @Override - public boolean subscribe(IProvider provider, Map paramMap) { - boolean success = super.subscribe(provider, paramMap); - if (success) { - fireProviderConnectionEvent(provider, PipeConnectionEvent.PROVIDER_CONNECT_PUSH, paramMap); - } - return success; - } - - /** {@inheritDoc} */ - public IMessage pullMessage() { - return null; - } - - /** {@inheritDoc} */ - public IMessage pullMessage(long wait) { - return null; - } - - /** - * Pushes a message out to all the PushableConsumers. - * - * @param message the message to be pushed to consumers. - */ - public void pushMessage(IMessage message) throws IOException { - for (IConsumer consumer : consumers) { - try { - IPushableConsumer pcon = (IPushableConsumer) consumer; - pcon.pushMessage(this, message); - } catch (Throwable t) { - if (t instanceof IOException) { - // Pass this along - throw (IOException) t; - } - log.error("Exception when pushing message to consumer", t); - } - } - } -} diff --git a/src/main/java/org/red5/server/messaging/OOBControlMessage.java b/src/main/java/org/red5/server/messaging/OOBControlMessage.java deleted file mode 100644 index f6c2e3be0..000000000 --- a/src/main/java/org/red5/server/messaging/OOBControlMessage.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -import java.io.Serializable; -import java.util.Map; - -/** - * Out-of-band control message used by inter-components communication - * which are connected with pipes. - * Out-of-band data is a separate data stream used for specific purposes (in TCP - * it's referenced as "urgent data"), like lifecycle control. - * - * 'Target' is used to represent the receiver who may be - * interested for receiving. It's a string of any form. - * XXX shall we design a standard form for Target, like "class.instance"? - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public class OOBControlMessage implements Serializable { - private static final long serialVersionUID = -6037348177653934300L; - /** - * Target - */ - private String target; - /** - * Service name - */ - private String serviceName; - /** - * Service params name - */ - private Map serviceParamMap; - /** - * Result - */ - private Object result; - - /** - * Getter for property 'serviceName'. - * - * @return Value for property 'serviceName'. - */ - public String getServiceName() { - return serviceName; - } - - /** - * Setter for property 'serviceName'. - * - * @param serviceName Value to set for property 'serviceName'. - */ - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - /** - * Getter for property 'serviceParamMap'. - * - * @return Value for property 'serviceParamMap'. - */ - public Map getServiceParamMap() { - return serviceParamMap; - } - - /** - * Setter for property 'serviceParamMap'. - * - * @param serviceParamMap Value to set for property 'serviceParamMap'. - */ - public void setServiceParamMap(Map serviceParamMap) { - this.serviceParamMap = serviceParamMap; - } - - /** - * Getter for property 'target'. - * - * @return Value for property 'target'. - */ - public String getTarget() { - return target; - } - - /** - * Setter for property 'target'. - * - * @param target Value to set for property 'target'. - */ - public void setTarget(String target) { - this.target = target; - } - - /** - * Getter for property 'result'. - * - * @return Value for property 'result'. - */ - public Object getResult() { - return result; - } - - /** - * Setter for property 'result'. - * - * @param result Value to set for property 'result'. - */ - public void setResult(Object result) { - this.result = result; - } -} diff --git a/src/main/java/org/red5/server/messaging/PipeConnectionEvent.java b/src/main/java/org/red5/server/messaging/PipeConnectionEvent.java deleted file mode 100644 index 3657198d1..000000000 --- a/src/main/java/org/red5/server/messaging/PipeConnectionEvent.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.messaging; - -import java.util.ArrayList; -import java.util.EventObject; -import java.util.List; -import java.util.Map; - -/** - * Event object corresponds to the connect/disconnect events - * among providers/consumers and pipes. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public class PipeConnectionEvent extends EventObject { - - private static final long serialVersionUID = 9078843765378168072L; - - private List taskList = new ArrayList(3); - - /** - * A provider connects as pull mode. - */ - public static final int PROVIDER_CONNECT_PULL = 0; - - /** - * A provider connects as push mode. - */ - public static final int PROVIDER_CONNECT_PUSH = 1; - - /** - * A provider disconnects. - */ - public static final int PROVIDER_DISCONNECT = 2; - - /** - * A consumer connects as pull mode. - */ - public static final int CONSUMER_CONNECT_PULL = 3; - - /** - * A consumer connects as push mode. - */ - public static final int CONSUMER_CONNECT_PUSH = 4; - - /** - * A consumer disconnects. - */ - public static final int CONSUMER_DISCONNECT = 5; - - /** - * Provider - */ - private transient IProvider provider; - - /** - * Consumer - */ - private transient IConsumer consumer; - - /** - * Event type - */ - private int type; - - /** - * Parameters map - */ - private Map paramMap; - - /** - * Construct an object with the specific pipe as the - * source - * @param source A pipe that triggers this event. - */ - public PipeConnectionEvent(Object source) { - super(source); - } - - /** - * Return pipe connection provider - * @return Provider - */ - public IProvider getProvider() { - return provider; - } - - /** - * Setter for pipe connection provider - * @param provider Provider - */ - public void setProvider(IProvider provider) { - this.provider = provider; - } - - /** - * Return pipe connection consumer - * @return Consumer - */ - public IConsumer getConsumer() { - return consumer; - } - - /** - * Setter for pipe connection consumer - * @param consumer Consumer - */ - public void setConsumer(IConsumer consumer) { - this.consumer = consumer; - } - - /** - * Return event type - * @return Event type - */ - public int getType() { - return type; - } - - /** - * Setter for event type - * @param type Event type - */ - public void setType(int type) { - this.type = type; - } - - /** - * Return event parameters as Map - * @return Event parameters as Map - */ - public Map getParamMap() { - return paramMap; - } - - /** - * Setter for event parameters map - * @param paramMap Event parameters as Map - */ - public void setParamMap(Map paramMap) { - this.paramMap = paramMap; - } - - /** - * Add task to list - * @param task Task to add - */ - public void addTask(Runnable task) { - taskList.add(task); - } - - /** - * Return list of tasks - * @return List of tasks - */ - List getTaskList() { - return taskList; - } -} diff --git a/src/main/java/org/red5/server/net/ICommand.java b/src/main/java/org/red5/server/net/ICommand.java deleted file mode 100644 index 55e0c1372..000000000 --- a/src/main/java/org/red5/server/net/ICommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.red5.server.net; - -import java.util.Map; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.service.IServiceCall; - -/** - * Represents a "command" sent to or received from an end-point. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public interface ICommand { - - int getTransactionId(); - - IServiceCall getCall(); - - Map getConnectionParams(); - - IoBuffer getData(); - -} diff --git a/src/main/java/org/red5/server/net/IConnectionManager.java b/src/main/java/org/red5/server/net/IConnectionManager.java deleted file mode 100644 index 83136cf60..000000000 --- a/src/main/java/org/red5/server/net/IConnectionManager.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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; - -import java.util.Collection; - -public interface IConnectionManager { - - /** - * Returns a connection matching the given client id. - * - * @param clientId - * @return connection - */ - T getConnection(int clientId); - - /** - * Adds a connection. - * - * @param conn - */ - void setConnection(T conn); - - /** - * Returns a connection matching the given session id. - * - * @param sessionId - * @return connection - */ - T getConnectionBySessionId(String sessionId); - - /** - * Returns all the current connections. It doesn't remove anything. - * - * @return list of connections - */ - Collection getAllConnections(); - - /** - * Creates a connection based on the given type class. - * - * @param connCls - * @return connection - */ - T createConnection(Class connCls); - - /** - * Creates a connection of the type specified with associated session id. - * - * @param connCls - * @param sessionId - */ - T createConnection(Class connCls, String sessionId); - - /** - * Removes a connection matching the client id specified. If found, the connection - * will be returned. - * - * @param clientId - * @return connection - */ - T removeConnection(int clientId); - - /** - * Removes a connection by the given sessionId. - * - * @param sessionId - * @return connection that was removed - */ - T removeConnection(String sessionId); - - /** - * Removes all the connections from the set. - * @return connections - */ - Collection removeConnections(); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/net/IHandshake.java b/src/main/java/org/red5/server/net/IHandshake.java deleted file mode 100644 index e7c5ce5e9..000000000 --- a/src/main/java/org/red5/server/net/IHandshake.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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; - -import org.apache.mina.core.buffer.IoBuffer; - -/** - * Base interface for Handshake classes. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public interface IHandshake { - - public IoBuffer doHandshake(IoBuffer input); - - public boolean validate(IoBuffer input); -} diff --git a/src/main/java/org/red5/server/net/protocol/HandshakeFailedException.java b/src/main/java/org/red5/server/net/protocol/HandshakeFailedException.java deleted file mode 100644 index 5474906b0..000000000 --- a/src/main/java/org/red5/server/net/protocol/HandshakeFailedException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.protocol; - -public class HandshakeFailedException extends ProtocolException { - - /** - * - */ - private static final long serialVersionUID = 8255789603304183796L; - - /** - * Create handshake failed exception with given message - * - * @param message message - */ - public HandshakeFailedException(String message) { - super(message); - } - -} diff --git a/src/main/java/org/red5/server/net/protocol/ProtocolException.java b/src/main/java/org/red5/server/net/protocol/ProtocolException.java deleted file mode 100644 index 8559a3650..000000000 --- a/src/main/java/org/red5/server/net/protocol/ProtocolException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.protocol; - -public class ProtocolException extends RuntimeException { - - /** - * Base exception for all protocol exceptions. - */ - private static final long serialVersionUID = -5380844081848027068L; - - /** - * Create protocol exception with given message. - * - * @param message message - */ - public ProtocolException(String message) { - super(message); - } - - /** - * Create protocol exception with given message and cause. - * - * @param message message - * @param cause cause - */ - public ProtocolException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/src/main/java/org/red5/server/net/protocol/RTMPDecodeState.java b/src/main/java/org/red5/server/net/protocol/RTMPDecodeState.java deleted file mode 100644 index bf5b20e0a..000000000 --- a/src/main/java/org/red5/server/net/protocol/RTMPDecodeState.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.protocol; - -/** - * Represents current decode state of the protocol. - */ -public class RTMPDecodeState { - - /** - * Session id to which this decoding state belongs. - */ - public String sessionId; - - /** - * Decoding finished successfully state constant. - */ - public static byte DECODER_OK = 0x00; - - /** - * Decoding continues state constant. - */ - public static byte DECODER_CONTINUE = 0x01; - - /** - * Decoder is buffering state constant. - */ - public static byte DECODER_BUFFER = 0x02; - - /** - * Classes like the RTMP state object will extend this marker interface. - */ - private int decoderBufferAmount; - - /** - * Current decoder state, decoder is stopped by default. - */ - private byte decoderState = DECODER_OK; - - public RTMPDecodeState(String sessionId) { - this.sessionId = sessionId; - } - - /** - * Returns current buffer amount. - * - * @return Buffer amount - */ - public int getDecoderBufferAmount() { - return decoderBufferAmount; - } - - /** - * Specifies buffer decoding amount - * - * @param amount Buffer decoding amount - */ - public void bufferDecoding(int amount) { - decoderState = DECODER_BUFFER; - decoderBufferAmount = amount; - } - - /** - * Set decoding state as "needed to be continued". - */ - public void continueDecoding() { - decoderState = DECODER_CONTINUE; - } - - /** - * Checks whether remaining buffer size is greater or equal than buffer amount and so if it makes sense to start decoding. - * - * @param remaining Remaining buffer size - * @return true if there is data to decode, false otherwise - */ - public boolean canStartDecoding(int remaining) { - return remaining >= decoderBufferAmount; - } - - /** - * Starts decoding. Sets state to "ready" and clears buffer amount. - */ - public void startDecoding() { - decoderState = DECODER_OK; - decoderBufferAmount = 0; - } - - /** - * Checks whether decoding is complete. - * - * @return true if decoding has finished, false otherwise - */ - public boolean hasDecodedObject() { - return (decoderState == DECODER_OK); - } - - /** - * Checks whether decoding process can be continued. - * - * @return true if decoding can be continued, false otherwise - */ - public boolean canContinueDecoding() { - return (decoderState != DECODER_BUFFER); - } - - /** - * @return the sessionId - */ - public String getSessionId() { - return sessionId; - } - - /** - * @param sessionId the sessionId to set - */ - public void setSessionId(String sessionId) { - this.sessionId = sessionId; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "RTMPDecodeState [sessionId=" + sessionId + ", decoderState=" + decoderState + ", decoderBufferAmount=" + decoderBufferAmount + "]"; - } - -} diff --git a/src/main/java/org/red5/server/net/remoting/IRemotingCallback.java b/src/main/java/org/red5/server/net/remoting/IRemotingCallback.java deleted file mode 100644 index 32a6be3f3..000000000 --- a/src/main/java/org/red5/server/net/remoting/IRemotingCallback.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.remoting; - -/** - * Callback for asynchronous remoting calls. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IRemotingCallback { - - /** - * The result of a remoting call has been received. - * - * @param client Remoting client - * @param method Remote method name - * @param params Call parameters - * @param result Call result - */ - public void resultReceived(RemotingClient client, String method, - Object[] params, Object result); - - /** - * An error occured while performing the remoting call. - * - * @param client Remoting client - * @param method Remoting method - * @param params Call parameters - * @param error Call result - */ - public void errorReceived(RemotingClient client, String method, - Object[] params, Throwable error); - -} diff --git a/src/main/java/org/red5/server/net/remoting/RemotingClient.java b/src/main/java/org/red5/server/net/remoting/RemotingClient.java deleted file mode 100644 index 7ada52682..000000000 --- a/src/main/java/org/red5/server/net/remoting/RemotingClient.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.remoting; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.util.EntityUtils; -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.io.amf.Input; -import org.red5.io.amf.Output; -import org.red5.io.client.IRemotingClient; -import org.red5.io.object.Deserializer; -import org.red5.io.object.RecordSet; -import org.red5.io.object.Serializer; -import org.red5.server.util.HttpConnectionUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Client interface for remoting calls. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * @author Paul Gregoire (mondain@gmail.com) - */ -public class RemotingClient implements IRemotingClient { - - protected static Logger log = LoggerFactory.getLogger(RemotingClient.class); - - /** Default timeout to use. */ - public static final int DEFAULT_TIMEOUT = 30000; - - /** Content MIME type for HTTP requests. */ - protected static final String CONTENT_TYPE = "application/x-amf"; - - /** HTTP client for remoting calls. */ - protected HttpClient client; - - /** Url to connect to. */ - protected String url; - - /** Additional string to use while connecting. */ - protected String appendToUrl = ""; - - /** Headers to send to the server. */ - protected Map headers = new ConcurrentHashMap(); - - /** Thread pool to use for asynchronous requests. */ - protected static ExecutorService executor; - - /** Maximum pool threads */ - protected int poolSize = 1; - - /** - * Dummy constructor used by the spring configuration. - */ - public RemotingClient() { - log.debug("RemotingClient created"); - } - - /** - * Create new remoting client for the given url. - * - * @param url URL to connect to - */ - public RemotingClient(String url) { - this(url, DEFAULT_TIMEOUT); - log.debug("RemotingClient created - url: {}", url); - } - - /** - * Create new remoting client for the given url and given timeout. - * - * @param url URL to connect to - * @param timeout Timeout for one request in milliseconds - */ - public RemotingClient(String url, int timeout) { - client = HttpConnectionUtil.getClient(timeout); - this.url = url; - log.debug("RemotingClient created - url: {} timeout: {}", url, timeout); - } - - public int getPoolSize() { - return poolSize; - } - - public void setPoolSize(int poolSize) { - this.poolSize = poolSize; - executor = Executors.newFixedThreadPool(poolSize); - } - - /** - * Encode the method call. - * - * @param method - * Remote method being called - * @param params - * Method parameters - * @return Byte buffer with data to perform remoting call - */ - private IoBuffer encodeInvoke(String method, Object[] params) { - log.debug("RemotingClient encodeInvoke - method: {} params: {}", method, params); - IoBuffer result = IoBuffer.allocate(1024); - result.setAutoExpand(true); - - // XXX: which is the correct version? - result.putShort((short) 0); - // Headers - Collection hdr = headers.values(); - result.putShort((short) hdr.size()); - for (RemotingHeader header : hdr) { - Output.putString(result, header.name); - result.put(header.required ? (byte) 0x01 : (byte) 0x00); - IoBuffer tmp = IoBuffer.allocate(1024); - tmp.setAutoExpand(true); - Output tmpOut = new Output(tmp); - Serializer.serialize(tmpOut, header.data); - tmp.flip(); - // Size of header data - result.putInt(tmp.limit()); - // Header data - result.put(tmp); - tmp.free(); - tmp = null; - } - // One body - result.putShort((short) 1); - - // Method name - Output.putString(result, method); - - // Client callback for response - Output.putString(result, ""); - - // Serialize parameters - IoBuffer tmp = IoBuffer.allocate(1024); - tmp.setAutoExpand(true); - Output tmpOut = new Output(tmp); - - //if the params are null send the NULL AMF type - //this should fix APPSERVER-296 - if (params == null) { - tmpOut.writeNull(); - } else { - tmpOut.writeArray(params); - } - tmp.flip(); - - // Store size and parameters - result.putInt(tmp.limit()); - result.put(tmp); - tmp.free(); - tmp = null; - - result.flip(); - return result; - } - - /** - * Process any headers sent in the response. - * - * @param in - * Byte buffer with response data - */ - @SuppressWarnings("static-access") - protected void processHeaders(IoBuffer in) { - log.debug("RemotingClient processHeaders - buffer limit: {}", (in != null ? in.limit() : 0)); - int version = in.getUnsignedShort(); // skip - log.debug("Version: {}", version); - // the version by now, AMF3 is not yet implemented - int count = in.getUnsignedShort(); - log.debug("Count: {}", count); - Input input = new Input(in); - for (int i = 0; i < count; i++) { - String name = input.getString(in); - //String name = deserializer.deserialize(input, String.class); - log.debug("Name: {}", name); - boolean required = (in.get() == 0x01); - log.debug("Required: {}", required); - int len = in.getInt(); - log.debug("Length: {}", len); - Object value = Deserializer.deserialize(input, Object.class); - log.debug("Value: {}", value); - - // XXX: this is pretty much untested!!! - if (RemotingHeader.APPEND_TO_GATEWAY_URL.equals(name)) { - // Append string to gateway url - appendToUrl = (String) value; - } else if (RemotingHeader.REPLACE_GATEWAY_URL.equals(name)) { - // Replace complete gateway url - url = (String) value; - // XXX: reset the ) { - @SuppressWarnings("unchecked") - Map valueMap = (Map) value; - RemotingHeader header = new RemotingHeader((String) valueMap.get("name"), (Boolean) valueMap.get("mustUnderstand"), valueMap.get("data")); - headers.put(header.name, header); - } else { - log.error("Expected Map but received {}", value); - } - } else { - log.warn("Unsupported remoting header \"{}\" received with value \"{}\"", name, value); - } - } - } - - /** - * Decode response received from remoting server. - * - * @param data - * Result data to decode - * @return Object deserialized from byte buffer data - */ - private Object decodeResult(IoBuffer data) { - log.debug("decodeResult - data limit: {}", (data != null ? data.limit() : 0)); - processHeaders(data); - int count = data.getUnsignedShort(); - if (count != 1) { - throw new RuntimeException("Expected exactly one result but got " + count); - } - Input input = new Input(data); - String target = input.getString(); // expect "/onResult" - log.debug("Target: {}", target); - String nullString = input.getString(); // expect "null" - log.debug("Null string: {}", nullString); - // Read return value - return Deserializer.deserialize(input, Object.class); - } - - /** - * Send authentication data with each remoting request. - * - * @param userid User identifier - * @param password Password - */ - public void setCredentials(String userid, String password) { - Map data = new HashMap(); - data.put("userid", userid); - data.put("password", password); - RemotingHeader header = new RemotingHeader(RemotingHeader.CREDENTIALS, true, data); - headers.put(RemotingHeader.CREDENTIALS, header); - } - - /** - * Stop sending authentication data. - */ - public void resetCredentials() { - removeHeader(RemotingHeader.CREDENTIALS); - } - - /** - * Send an additional header to the server. - * - * @param name Header name - * @param required Header required? - * @param value Header body - */ - public void addHeader(String name, boolean required, Object value) { - RemotingHeader header = new RemotingHeader(name, required, value); - headers.put(name, header); - } - - /** - * Stop sending a given header. - * - * @param name Header name - */ - public void removeHeader(String name) { - headers.remove(name); - } - - /** - * Invoke a method synchronously on the remoting server. - * - * @param method Method name - * @param params Parameters passed to method - * @return the result of the method call - */ - public Object invokeMethod(String method, Object[] params) { - log.debug("invokeMethod url: {}", (url + appendToUrl)); - IoBuffer resultBuffer = null; - IoBuffer data = encodeInvoke(method, params); - //setup POST - HttpPost post = null; - try { - post = new HttpPost(url + appendToUrl); - post.addHeader("Content-Type", CONTENT_TYPE); - post.setEntity(new InputStreamEntity(data.asInputStream(), data.limit())); - // execute the method - HttpResponse response = client.execute(post); - int code = response.getStatusLine().getStatusCode(); - log.debug("HTTP response code: {}", code); - if (code / 100 != 2) { - throw new RuntimeException("Didn't receive success from remoting server"); - } else { - HttpEntity entity = response.getEntity(); - if (entity != null) { - //fix for Trac #676 - int contentLength = (int) entity.getContentLength(); - //default the content length to 16 if post doesn't contain a good value - if (contentLength < 1) { - contentLength = 16; - } - // get the response as bytes - byte[] bytes = EntityUtils.toByteArray(entity); - resultBuffer = IoBuffer.wrap(bytes); - Object result = decodeResult(resultBuffer); - if (result instanceof RecordSet) { - // Make sure we can retrieve paged results - ((RecordSet) result).setRemotingClient(this); - } - return result; - } - } - } catch (Exception ex) { - log.error("Error while invoking remoting method.", ex); - post.abort(); - } finally { - if (resultBuffer != null) { - resultBuffer.free(); - resultBuffer = null; - } - data.free(); - data = null; - } - return null; - } - - /** - * Invoke a method asynchronously on the remoting server. - * - * @param method Method name - * @param methodParams Parameters passed to method - * @param callback Callback - */ - public void invokeMethod(String method, Object[] methodParams, IRemotingCallback callback) { - try { - RemotingWorker worker = new RemotingWorker(this, method, methodParams, callback); - executor.execute(worker); - } catch (Exception err) { - log.warn("Exception invoking method: {}", method, err); - } - } - - /** - * Worker class that is used for asynchronous remoting calls. - */ - public final static class RemotingWorker implements Runnable { - - private final RemotingClient client; - - private final String method; - - private final Object[] params; - - private final IRemotingCallback callback; - - /** - * Execute task. - * - * @param client Remoting client - * @param method Method name - * @param params Parameters to pass to method on call - * @param callback Callback - */ - public RemotingWorker(RemotingClient client, String method, Object[] params, IRemotingCallback callback) { - this.client = client; - this.method = method; - this.params = params; - this.callback = callback; - } - - public void run() { - try { - Object result = client.invokeMethod(method, params); - callback.resultReceived(client, method, params, result); - } catch (Exception err) { - callback.errorReceived(client, method, params, err); - } - } - } - -} diff --git a/src/main/java/org/red5/server/net/remoting/RemotingHeader.java b/src/main/java/org/red5/server/net/remoting/RemotingHeader.java deleted file mode 100644 index 531e1671f..000000000 --- a/src/main/java/org/red5/server/net/remoting/RemotingHeader.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.remoting; - -import org.red5.server.api.remoting.IRemotingHeader; - -/** - * Remoting header to be sent to a server. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public class RemotingHeader implements IRemotingHeader { - - /** - * The name of the header. - */ - protected String name; - - /** - * Is this header required? - */ - protected boolean required; - - /** - * The actual data of the header. - */ - protected Object data; - - /** - * Create a new header to be sent through remoting. - * - * @param name Header name - * @param required Header required? - * @param data Header data - */ - public RemotingHeader(String name, boolean required, Object data) { - this.name = name; - this.required = required; - this.data = data; - } - - /** {@inheritDoc} */ - public boolean getMustUnderstand() { - return required; - } - - /** {@inheritDoc} */ - public String getName() { - return name; - } - - /** {@inheritDoc} */ - public Object getValue() { - return data; - } -} diff --git a/src/main/java/org/red5/server/net/rtmp/BaseRTMPHandler.java b/src/main/java/org/red5/server/net/rtmp/BaseRTMPHandler.java deleted file mode 100644 index bd49d9dc0..000000000 --- a/src/main/java/org/red5/server/net/rtmp/BaseRTMPHandler.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import java.util.HashSet; -import java.util.Set; - -import org.red5.io.object.StreamAction; -import org.red5.server.api.event.IEventDispatcher; -import org.red5.server.api.service.IPendingServiceCall; -import org.red5.server.api.service.IPendingServiceCallback; -import org.red5.server.api.service.IServiceCall; -import org.red5.server.api.stream.IClientStream; -import org.red5.server.net.ICommand; -import org.red5.server.net.rtmp.codec.RTMP; -import org.red5.server.net.rtmp.event.BytesRead; -import org.red5.server.net.rtmp.event.ChunkSize; -import org.red5.server.net.rtmp.event.ClientBW; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.Ping; -import org.red5.server.net.rtmp.event.ServerBW; -import org.red5.server.net.rtmp.event.Unknown; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.net.rtmp.message.Packet; -import org.red5.server.net.rtmp.status.StatusCodes; -import org.red5.server.so.SharedObjectMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Base class for all RTMP handlers. - * - * @author The Red5 Project - */ -public abstract class BaseRTMPHandler implements IRTMPHandler, Constants, StatusCodes { - - private static Logger log = LoggerFactory.getLogger(BaseRTMPHandler.class); - - /** {@inheritDoc} */ - public void connectionOpened(RTMPConnection conn) { - log.trace("connectionOpened - conn: {} state: {}", conn, conn.getState()); - conn.open(); - } - - /** {@inheritDoc} */ - public void messageReceived(RTMPConnection conn, Packet packet) throws Exception { - log.trace("Connection: {}", conn.getSessionId()); - if (conn != null) { - IRTMPEvent message = null; - try { - message = packet.getMessage(); - final Header header = packet.getHeader(); - final int streamId = header.getStreamId(); - final Channel channel = conn.getChannel(header.getChannelId()); - final IClientStream stream = conn.getStreamById(streamId); - log.trace("Message received - stream id: {} channel: {} header: {}", streamId, channel.getId(), header); - // set stream id on the connection - conn.setStreamId(streamId); - // increase number of received messages - conn.messageReceived(); - // set the source of the message - message.setSource(conn); - // process based on data type - final byte headerDataType = header.getDataType(); - log.trace("Header / message data type: {}", headerDataType); - switch (headerDataType) { - case TYPE_AGGREGATE: - log.debug("Aggregate type data - header timer: {} size: {}", header.getTimer(), header.getSize()); - case TYPE_AUDIO_DATA: - case TYPE_VIDEO_DATA: - // mark the event as from a live source - // log.trace("Marking message as originating from a Live source"); - message.setSourceType(Constants.SOURCE_TYPE_LIVE); - // NOTE: If we respond to "publish" with "NetStream.Publish.BadName", - // the client sends a few stream packets before stopping. We need to ignore them - if (stream != null) { - ((IEventDispatcher) stream).dispatchEvent(message); - } - break; - case TYPE_FLEX_SHARED_OBJECT: - case TYPE_SHARED_OBJECT: - onSharedObject(conn, channel, header, (SharedObjectMessage) message); - break; - case TYPE_INVOKE: - case TYPE_FLEX_MESSAGE: - onCommand(conn, channel, header, (Invoke) message); - IPendingServiceCall call = ((Invoke) message).getCall(); - if (message.getHeader().getStreamId() != 0 && call.getServiceName() == null && StreamAction.PUBLISH.equals(call.getServiceMethodName())) { - if (stream != null) { - // Only dispatch if stream really was created - ((IEventDispatcher) stream).dispatchEvent(message); - } - } - break; - case TYPE_NOTIFY: // like an invoke, but does not return - // anything and has a invoke / transaction - // id of 0 - case TYPE_FLEX_STREAM_SEND: - if (((Notify) message).getData() != null && stream != null) { - // Stream metadata - ((IEventDispatcher) stream).dispatchEvent(message); - } else { - onCommand(conn, channel, header, (Notify) message); - } - break; - case TYPE_PING: - onPing(conn, channel, header, (Ping) message); - break; - case TYPE_BYTES_READ: - onStreamBytesRead(conn, channel, header, (BytesRead) message); - break; - case TYPE_CHUNK_SIZE: - onChunkSize(conn, channel, header, (ChunkSize) message); - break; - case Constants.TYPE_CLIENT_BANDWIDTH: // onBWDone / peer bw - log.debug("Client bandwidth: {}", message); - onClientBandwidth(conn, channel, (ClientBW) message); - break; - case Constants.TYPE_SERVER_BANDWIDTH: // window ack size - log.debug("Server bandwidth: {}", message); - onServerBandwidth(conn, channel, (ServerBW) message); - break; - default: - log.debug("Unknown type: {}", header.getDataType()); - } - if (message instanceof Unknown) { - log.info("Message type unknown: {}", message); - } - } catch (Throwable t) { - log.error("Exception", t); - } - // XXX this may be causing 'missing' data if previous methods are - // not making copies before buffering etc.. - if (message != null) { - message.release(); - } - } - } - - /** {@inheritDoc} */ - public void messageSent(RTMPConnection conn, Packet packet) { - log.trace("Message sent"); - // increase number of sent messages - conn.messageSent(packet); - } - - /** {@inheritDoc} */ - public void connectionClosed(RTMPConnection conn) { - log.debug("connectionClosed: {}", conn.getSessionId()); - if (conn.getStateCode() != RTMP.STATE_DISCONNECTED) { - // inform any callbacks for pending calls that the connection is - // closed - conn.sendPendingServiceCallsCloseError(); - // close the connection - if (conn.getStateCode() != RTMP.STATE_DISCONNECTING) { - conn.close(); - } - // set as disconnected - conn.setStateCode(RTMP.STATE_DISCONNECTED); - // remove from the manager - RTMPConnManager.getInstance().removeConnection(conn.getSessionId()); - } - log.trace("connectionClosed: {}", conn); - } - - /** - * Return hostname for URL. - * - * @param url - * URL - * @return Hostname from that URL - */ - protected String getHostname(String url) { - log.debug("url: {}", url); - String[] parts = url.split("/"); - if (parts.length == 2) { - return ""; - } else { - String host = parts[2]; - // strip out default port in case the client added the port - // explicitly - if (host.endsWith(":1935")) { - // remove default port from connection string - return host.substring(0, host.length() - 5); - } - return host; - } - } - - /** - * Handler for pending call result. Dispatches results to all pending call - * handlers. - * - * @param conn - * Connection - * @param invoke - * Pending call result event context - */ - protected void handlePendingCallResult(RTMPConnection conn, Invoke invoke) { - final IServiceCall call = invoke.getCall(); - final IPendingServiceCall pendingCall = conn.retrievePendingCall(invoke.getTransactionId()); - if (pendingCall != null) { - // The client sent a response to a previously made call. - Object[] args = call.getArguments(); - if (args != null && args.length > 0) { - // TODO: can a client return multiple results? - pendingCall.setResult(args[0]); - } - Set callbacks = pendingCall.getCallbacks(); - if (!callbacks.isEmpty()) { - HashSet tmp = new HashSet(); - tmp.addAll(callbacks); - for (IPendingServiceCallback callback : tmp) { - try { - callback.resultReceived(pendingCall); - } catch (Exception e) { - log.error("Error while executing callback {}", callback, e); - } - } - } - } - } - - /** - * Chunk size change event handler. Abstract, to be implemented in - * subclasses. - * - * @param conn - * Connection - * @param channel - * Channel - * @param source - * Header - * @param chunkSize - * New chunk size - */ - protected abstract void onChunkSize(RTMPConnection conn, Channel channel, Header source, ChunkSize chunkSize); - - /** - * Command event handler, which current consists of an Invoke or Notify type - * object. - * - * @param conn - * Connection - * @param channel - * Channel - * @param source - * Header - * @param command - * event context - */ - protected abstract void onCommand(RTMPConnection conn, Channel channel, Header source, ICommand command); - - /** - * Ping event handler. - * - * @param conn - * Connection - * @param channel - * Channel - * @param source - * Header - * @param ping - * Ping event context - */ - protected abstract void onPing(RTMPConnection conn, Channel channel, Header source, Ping ping); - - /** - * Server bandwidth / Window ACK size event handler. - * - * @param conn - * Connection - * @param channel - * Channel - * @param message - * ServerBW - */ - protected void onServerBandwidth(RTMPConnection conn, Channel channel, ServerBW message) { - - } - - /** - * Client bandwidth / Peer bandwidth set event handler. - * - * @param conn - * Connection - * @param channel - * Channel - * @param message - * ClientBW - */ - protected void onClientBandwidth(RTMPConnection conn, Channel channel, ClientBW message) { - - } - - /** - * Stream bytes read event handler. - * - * @param conn - * Connection - * @param channel - * Channel - * @param source - * Header - * @param streamBytesRead - * Bytes read event context - */ - protected void onStreamBytesRead(RTMPConnection conn, Channel channel, Header source, BytesRead streamBytesRead) { - conn.receivedBytesRead(streamBytesRead.getBytesRead()); - } - - /** - * Shared object event handler. - * - * @param conn - * Connection - * @param channel - * Channel - * @param source - * Header - * @param message - * Shared object message - */ - protected abstract void onSharedObject(RTMPConnection conn, Channel channel, Header source, SharedObjectMessage message); - -} diff --git a/src/main/java/org/red5/server/net/rtmp/Channel.java b/src/main/java/org/red5/server/net/rtmp/Channel.java deleted file mode 100644 index bdb965f4f..000000000 --- a/src/main/java/org/red5/server/net/rtmp/Channel.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.stream.IClientStream; -import org.red5.server.api.stream.IRtmpSampleAccess; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.net.rtmp.message.Packet; -import org.red5.server.net.rtmp.status.Status; -import org.red5.server.net.rtmp.status.StatusCodes; -import org.red5.server.service.Call; -import org.red5.server.service.PendingCall; -import org.red5.server.stream.IStreamData; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Identified connection that transfers packets. - */ -public class Channel { - - protected static Logger log = LoggerFactory.getLogger(Channel.class); - - private final static String CALL_ON_STATUS = "onStatus"; - - /** - * RTMP connection used to transfer packets. - */ - private final RTMPConnection connection; - - /** - * Channel id - */ - private final int id; - - /** - * Creates channel from connection and channel id - * @param conn Connection - * @param channelId Channel id - */ - public Channel(RTMPConnection conn, int channelId) { - assert(conn != null); - connection = conn; - id = channelId; - } - - /** - * Closes channel with this id on RTMP connection. - */ - public void close() { - connection.closeChannel(id); - } - - /** - * Getter for id. - * - * @return Channel ID - */ - public int getId() { - return id; - } - - /** - * Getter for RTMP connection. - * - * @return RTMP connection - */ - protected RTMPConnection getConnection() { - return connection; - } - - /** - * Writes packet from event data to RTMP connection. - * - * @param event Event data - */ - public void write(IRTMPEvent event) { - if (!connection.isClosed()) { - final IClientStream stream = connection.getStreamByChannelId(id); - if (id > 3 && stream == null) { - log.warn("Non-existant stream for channel id: {}, connection id: {} discarding: {}", id, connection.getSessionId()); - } - // if the stream is non-existant, the event will go out with stream id == 0 - final int streamId = (stream == null) ? 0 : stream.getStreamId(); - write(event, streamId); - } else { - log.debug("Associated connection {} is closed, cannot write to channel: {}", connection.getSessionId(), id); - } - } - - /** - * Writes packet from event data to RTMP connection and stream id. - * - * @param event Event data - * @param streamId Stream id - */ - private void write(IRTMPEvent event, int streamId) { - log.trace("write channel: {} stream id: {}", id, streamId); - final Header header = new Header(); - final Packet packet = new Packet(header, event); - header.setChannelId(id); - int ts = event.getTimestamp(); - if (ts != 0) { - header.setTimer(event.getTimestamp()); - } else { - // TODO may need to add generated timestamps at some point -// int timestamp = connection.getTimer(); -// header.setTimerBase(timestamp); -// event.setTimestamp(timestamp); - } - header.setStreamId(streamId); - header.setDataType(event.getDataType()); - // should use RTMPConnection specific method.. - //log.trace("Connection type for write: {}", connection.getClass().getName()); - connection.write(packet); - } - - /** - * Discard an event routed to this channel. - * - * @param event - */ - @SuppressWarnings("unused") - private void discard(IRTMPEvent event) { - if (event instanceof IStreamData) { - log.debug("Discarding: {}", ((IStreamData) event).toString()); - IoBuffer data = ((IStreamData) event).getData(); - if (data != null) { - log.trace("Freeing discarded event data"); - data.free(); - data = null; - } - } - event.setHeader(null); - } - - /** - * Sends status notification. - * - * @param status Status - */ - public void sendStatus(Status status) { - if (connection != null) { - final boolean andReturn = !status.getCode().equals(StatusCodes.NS_DATA_START); - final Invoke event = new Invoke(); - if (andReturn) { - final PendingCall call = new PendingCall(null, CALL_ON_STATUS, new Object[] { status }); - if (status.getCode().equals(StatusCodes.NS_PLAY_START)) { - IScope scope = connection.getScope(); - if (scope.getContext().getApplicationContext().containsBean(IRtmpSampleAccess.BEAN_NAME)) { - IRtmpSampleAccess sampleAccess = (IRtmpSampleAccess) scope.getContext().getApplicationContext().getBean(IRtmpSampleAccess.BEAN_NAME); - boolean videoAccess = sampleAccess.isVideoAllowed(scope); - boolean audioAccess = sampleAccess.isAudioAllowed(scope); - if (videoAccess || audioAccess) { - final Call call2 = new Call(null, "|RtmpSampleAccess", null); - Notify notify = new Notify(); - notify.setCall(call2); - notify.setData(IoBuffer.wrap(new byte[] { 0x01, (byte) (audioAccess ? 0x01 : 0x00), 0x01, (byte) (videoAccess ? 0x01 : 0x00) })); - write(notify, connection.getStreamIdForChannel(id)); - } - } - } - event.setCall(call); - } else { - final Call call = new Call(null, CALL_ON_STATUS, new Object[] { status }); - event.setCall(call); - } - // send directly to the corresponding stream as for some status codes, no stream has been created and thus "getStreamByChannelId" will fail - write(event, connection.getStreamIdForChannel(id)); - } - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "Channel [id=" + id + ", connection=" + connection + "]"; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/DeferredResult.java b/src/main/java/org/red5/server/net/rtmp/DeferredResult.java deleted file mode 100644 index c21b77dae..000000000 --- a/src/main/java/org/red5/server/net/rtmp/DeferredResult.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import java.lang.ref.WeakReference; - -import org.red5.server.api.service.IPendingServiceCall; -import org.red5.server.net.rtmp.event.Invoke; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Can be returned to delay returning the result of invoked methods. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public class DeferredResult { - /** - * Logger - */ - protected static Logger log = LoggerFactory.getLogger(DeferredResult.class); - - /** - * Weak reference to used channel - */ - private WeakReference channel; - - /** - * Pending call object - */ - private IPendingServiceCall call; - - /** - * Transaction id - */ - private int transactionId; - - /** - * Results sent flag - */ - private boolean resultSent = false; - - /** - * Set the result of a method call and send to the caller. - * - * @param result deferred result of the method call - */ - public void setResult(Object result) { - if (resultSent) { - throw new RuntimeException("You can only set the result once."); - } - this.resultSent = true; - Channel channel = this.channel.get(); - if (channel == null) { - log.warn("The client is no longer connected."); - return; - } - call.setResult(result); - Invoke reply = new Invoke(); - reply.setCall(call); - reply.setTransactionId(transactionId); - channel.write(reply); - channel.getConnection().unregisterDeferredResult(this); - } - - /** - * Check if the result has been sent to the client. - * - * @return true if the result has been sent, otherwise false - */ - public boolean wasSent() { - return resultSent; - } - - /** - * Setter for transaction id. - * - * @param id Invocation object identifier - */ - public void setTransactionId(int id) { - this.transactionId = id; - } - - /** - * Setter for service call. - * - * @param call Service call - */ - public void setServiceCall(IPendingServiceCall call) { - this.call = call; - } - - /** - * Setter for channel. - * - * @param channel Channel - */ - public void setChannel(Channel channel) { - this.channel = new WeakReference(channel); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/IRTMPHandler.java b/src/main/java/org/red5/server/net/rtmp/IRTMPHandler.java deleted file mode 100644 index 016341547..000000000 --- a/src/main/java/org/red5/server/net/rtmp/IRTMPHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import org.red5.server.net.rtmp.message.Packet; - -/** - * RTMP events handler - */ -public interface IRTMPHandler { - - /** - * Connection open event. - * - * @param conn Connection - */ - public void connectionOpened(RTMPConnection conn); - - /** - * Message received. - * - * @param conn Connection - * @param packet Packet containing an RTMP message - * @throws Exception - */ - public void messageReceived(RTMPConnection conn, Packet packet) throws Exception; - - /** - * Message sent. - * - * @param conn Connection - * @param packet RTMP message - */ - public void messageSent(RTMPConnection conn, Packet packet); - - /** - * Connection closed. - * - * @param conn Connection - */ - public void connectionClosed(RTMPConnection conn); - -} diff --git a/src/main/java/org/red5/server/net/rtmp/InboundHandshake.java b/src/main/java/org/red5/server/net/rtmp/InboundHandshake.java deleted file mode 100644 index 64e94e09e..000000000 --- a/src/main/java/org/red5/server/net/rtmp/InboundHandshake.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import java.security.KeyPair; -import java.util.Arrays; - -import javax.crypto.Cipher; -import javax.crypto.spec.SecretKeySpec; - -import org.apache.commons.codec.binary.Hex; -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.Red5; -import org.red5.server.net.rtmp.message.Constants; - -/** - * Performs handshaking for server connections. - * - * @author Paul Gregoire - */ -public class InboundHandshake extends RTMPHandshake { - - public InboundHandshake() { - super(); - } - - /** - * Generates response for versioned connections. - * - * @param input incoming RTMP bytes - * @return outgoing handshake - */ - public IoBuffer doHandshake(IoBuffer input) { - log.trace("doHandshake: {}", input); - if (log.isDebugEnabled()) { - log.debug("Player encryption byte: {}", handshakeType); - byte[] bIn = input.array(); - log.debug("Detecting flash player version {},{},{},{}", new Object[]{(bIn[4] & 0x0ff), (bIn[5] & 0x0ff), (bIn[6] & 0x0ff), (bIn[7] & 0x0ff)}); - //if the 5th byte is 0 then dont generate new-style handshake - if (log.isTraceEnabled()) { - log.trace("First few bytes (in): {},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}", new Object[] { - bIn[0], bIn[1], bIn[2], bIn[3], bIn[4], - bIn[5], bIn[6], bIn[7], bIn[8], bIn[9], - bIn[10], bIn[11], bIn[12], bIn[13], bIn[14], - bIn[15] }); - //client version hex - byte[] ver = new byte[4]; - System.arraycopy(bIn, 4, ver, 0, 4); - log.trace("Version string: {}", Hex.encodeHexString(ver)); - //dump - byte[] buf = new byte[KEY_LENGTH]; - System.arraycopy(bIn, 0, buf, 0, KEY_LENGTH); - log.trace("Hex: {}", Hex.encodeHexString(buf)); - } - } - input.mark(); - byte versionByte = input.get(4); - log.debug("Player version byte: {}", (versionByte & 0x0ff)); - input.reset(); - if (versionByte == 0) { - return generateUnversionedHandshake(input); - } - //create output buffer - IoBuffer output = IoBuffer.allocate(HANDSHAKE_SIZE_SERVER); - input.mark(); - //make sure this is a client we can communicate with - if (validate(input)) { - log.debug("Valid RTMP client detected"); - } else { - log.info("Invalid RTMP connection data detected, you may experience errors"); - } - input.reset(); - log.debug("Using new style handshake"); - input.mark(); - //create all the dh stuff and add to handshake bytes - prepareResponse(input); - input.reset(); - if (handshakeType == RTMPConnection.RTMP_ENCRYPTED) { - log.debug("Incoming public key [{}]: {}", incomingPublicKey.length, Hex.encodeHexString(incomingPublicKey)); - log.debug("Outgoing public key [{}]: {}", outgoingPublicKey.length, Hex.encodeHexString(outgoingPublicKey)); - byte[] sharedSecret = getSharedSecret(outgoingPublicKey, keyAgreement); - // create output cipher - byte[] digestOut = calculateHMAC_SHA256(outgoingPublicKey, sharedSecret); - try { - cipherOut = Cipher.getInstance("RC4"); - cipherOut.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(digestOut, 0, 16, "RC4")); - } catch (Exception e) { - log.warn("Encryption cipher creation failed", e); - } - // create input cipher - byte[] digestIn = calculateHMAC_SHA256(incomingPublicKey, sharedSecret); - try { - cipherIn = Cipher.getInstance("RC4"); - cipherIn.init(Cipher.DECRYPT_MODE, new SecretKeySpec(digestIn, 0, 16, "RC4")); - } catch (Exception e) { - log.warn("Decryption cipher creation failed", e); - } - // update 'encoder / decoder state' for the RC4 keys - // both parties *pretend* as if handshake part 2 (1536 bytes) was encrypted - // effectively this hides / discards the first few bytes of encrypted session - // which is known to increase the secure-ness of RC4 - // RC4 state is just a function of number of bytes processed so far - // that's why we just run 1536 arbitrary bytes through the keys below - byte[] dummyBytes = new byte[Constants.HANDSHAKE_SIZE]; - cipherIn.update(dummyBytes); - cipherOut.update(dummyBytes); - } - input.mark(); - //create the server digest - int serverDigestOffset = getDigestOffset(handshakeBytes); - byte[] tempBuffer = new byte[Constants.HANDSHAKE_SIZE - DIGEST_LENGTH]; - System.arraycopy(handshakeBytes, 0, tempBuffer, 0, serverDigestOffset); - System.arraycopy(handshakeBytes, serverDigestOffset + DIGEST_LENGTH, tempBuffer, serverDigestOffset, Constants.HANDSHAKE_SIZE - serverDigestOffset - DIGEST_LENGTH); - //calculate the hash - byte[] tempHash = calculateHMAC_SHA256(tempBuffer, GENUINE_FMS_KEY, 36); - //add the digest - System.arraycopy(tempHash, 0, handshakeBytes, serverDigestOffset, DIGEST_LENGTH); - //compute the challenge digest - byte[] inputBuffer = new byte[Constants.HANDSHAKE_SIZE - DIGEST_LENGTH]; - //log.debug("Before get: {}", input.position()); - input.get(inputBuffer); - //log.debug("After get: {}", input.position()); - int keyChallengeIndex = getDigestOffset(inputBuffer); - byte[] challengeKey = new byte[DIGEST_LENGTH]; - input.position(keyChallengeIndex); - input.get(challengeKey, 0, DIGEST_LENGTH); - input.reset(); - //compute key - tempHash = calculateHMAC_SHA256(challengeKey, GENUINE_FMS_KEY, 68); - //generate hash - byte[] randBytes = new byte[Constants.HANDSHAKE_SIZE - DIGEST_LENGTH]; - random.nextBytes(randBytes); - byte[] lastHash = calculateHMAC_SHA256(randBytes, tempHash, DIGEST_LENGTH); - //set handshake with encryption type - output.put(handshakeType); - output.put(handshakeBytes); - output.put(randBytes); - output.put(lastHash); - output.flip(); - if (log.isTraceEnabled()) { - byte[] bOut = output.array(); - log.trace("First few bytes (out): {},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}", new Object[] { - bOut[0], bOut[1], bOut[2], bOut[3], bOut[4], - bOut[5], bOut[6], bOut[7], bOut[8], bOut[9], - bOut[10], bOut[11], bOut[12], bOut[13], bOut[14], - bOut[15]}); - byte[] buf = new byte[KEY_LENGTH]; - System.arraycopy(bOut, 0, buf, 0, KEY_LENGTH); - log.trace("Hex: {}", Hex.encodeHexString(buf)); - } - return output; - } - - /** - * Generates response for non-versioned connections, such as those before FP9. - * - * @param input incoming RTMP bytes - * @return outgoing handshake - */ - private IoBuffer generateUnversionedHandshake(IoBuffer input) { - log.debug("Using old style (un-versioned) handshake"); - //save resource by only doing this after the first request - if (HANDSHAKE_PAD_BYTES == null) { - HANDSHAKE_PAD_BYTES = new byte[Constants.HANDSHAKE_SIZE - 4]; - //fill pad bytes - Arrays.fill(HANDSHAKE_PAD_BYTES, (byte) 0x00); - } - IoBuffer output = IoBuffer.allocate(HANDSHAKE_SIZE_SERVER); - //non-encrypted - output.put(RTMPConnection.RTMP_NON_ENCRYPTED); - //set server uptime in seconds - output.putInt((int) Red5.getUpTime() / 1000); //0x01 - output.put(RTMPHandshake.HANDSHAKE_PAD_BYTES); - output.put(input); - output.flip(); - return output; - } - - /** - * Creates the servers handshake bytes - */ - @Override - protected void createHandshakeBytes() { - handshakeBytes = new byte[Constants.HANDSHAKE_SIZE]; - //timestamp - handshakeBytes[0] = 0; - handshakeBytes[1] = 0; - handshakeBytes[2] = 0; - handshakeBytes[3] = 0; - //version (0x01020304) - handshakeBytes[4] = 1; - handshakeBytes[5] = 2; - handshakeBytes[6] = 3; - handshakeBytes[7] = 4; - //fill the rest with random bytes - byte[] rndBytes = new byte[Constants.HANDSHAKE_SIZE - 8]; - random.nextBytes(rndBytes); - //copy random bytes into our handshake array - System.arraycopy(rndBytes, 0, handshakeBytes, 8, (Constants.HANDSHAKE_SIZE - 8)); - } - - /** - * Gets the DH offset in the handshake bytes array based on validation scheme - * Generates DH keypair - * Adds public key to handshake bytes - * @param input - */ - private void prepareResponse(IoBuffer input) { - //put the clients input into a byte array - byte[] inputBuffer = new byte[input.limit()]; - input.get(inputBuffer); - //get the clients dh offset - int clientDHOffset = getDHOffset(inputBuffer); - log.trace("Incoming DH offset: {}", clientDHOffset); - //get the clients public key - outgoingPublicKey = new byte[KEY_LENGTH]; - System.arraycopy(inputBuffer, clientDHOffset, outgoingPublicKey, 0, KEY_LENGTH); - //get the servers dh offset - int serverDHOffset = getDHOffset(handshakeBytes); - log.trace("Outgoing DH offset: {}", serverDHOffset); - //create keypair - KeyPair keys = generateKeyPair(); - //get public key - incomingPublicKey = getPublicKey(keys); - //add to handshake bytes - System.arraycopy(incomingPublicKey, 0, handshakeBytes, serverDHOffset, KEY_LENGTH); - } - - /** - * Determines the validation scheme for given input. - * - * @param input - * @return true if client used a supported validation scheme, false if unsupported - */ - @Override - public boolean validate(IoBuffer input) { - byte[] pBuffer = new byte[input.remaining()]; - //put all the input bytes into our buffer - input.get(pBuffer, 0, input.remaining()); - if (validateScheme(pBuffer, 0)) { - validationScheme = 0; - log.debug("Selected scheme: 0"); - return true; - } - if (validateScheme(pBuffer, 1)) { - validationScheme = 1; - log.debug("Selected scheme: 1"); - return true; - } - log.error("Unable to validate client"); - return false; - } - - private boolean validateScheme(byte[] pBuffer, int scheme) { - int digestOffset = -1; - switch (scheme) { - case 0: - digestOffset = getDigestOffset0(pBuffer); - break; - case 1: - digestOffset = getDigestOffset1(pBuffer); - break; - default: - log.error("Unknown scheme: {}", scheme); - } - log.debug("Scheme: {} client digest offset: {}", scheme, digestOffset); - - byte[] tempBuffer = new byte[Constants.HANDSHAKE_SIZE - DIGEST_LENGTH]; - System.arraycopy(pBuffer, 0, tempBuffer, 0, digestOffset); - System.arraycopy(pBuffer, digestOffset + DIGEST_LENGTH, tempBuffer, digestOffset, Constants.HANDSHAKE_SIZE - digestOffset - DIGEST_LENGTH); - - byte[] tempHash = calculateHMAC_SHA256(tempBuffer, GENUINE_FP_KEY, 30); - log.debug("Temp: {}", Hex.encodeHexString(tempHash)); - - boolean result = true; - for (int i = 0; i < DIGEST_LENGTH; i++) { - //log.trace("Digest: {} Temp: {}", (pBuffer[digestOffset + i] & 0x0ff), (tempHash[i] & 0x0ff)); - if (pBuffer[digestOffset + i] != tempHash[i]) { - result = false; - break; - } - } - - return result; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/RTMPConnManager.java b/src/main/java/org/red5/server/net/rtmp/RTMPConnManager.java deleted file mode 100644 index b5c9adc6f..000000000 --- a/src/main/java/org/red5/server/net/rtmp/RTMPConnManager.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import java.lang.management.ManagementFactory; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.management.JMX; -import javax.management.ObjectName; - -import org.apache.mina.core.session.IoSession; -import org.red5.server.api.Red5; -import org.red5.server.api.scope.IBasicScope; -import org.red5.server.jmx.mxbeans.RTMPMinaTransportMXBean; -import org.red5.server.net.IConnectionManager; -import org.red5.server.net.rtmpt.RTMPTConnection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.scheduling.concurrent.CustomizableThreadFactory; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; - -/** - * Responsible for management and creation of RTMP based connections. - * - * @author The Red5 Project - */ -public class RTMPConnManager implements IConnectionManager, ApplicationContextAware, DisposableBean { - - private static final Logger log = LoggerFactory.getLogger(RTMPConnManager.class); - - protected static ApplicationContext applicationContext; - - private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, new CustomizableThreadFactory("ConnectionChecker-")); - - protected ConcurrentMap connMap = new ConcurrentHashMap(); - - protected AtomicInteger conns = new AtomicInteger(); - - protected static IConnectionManager instance; - - protected boolean debug; - - { - // create a scheduled job to check for dead or hung connections - executor.scheduleAtFixedRate(new Runnable() { - public void run() { - // count the connections that need closing - int closedConnections = 0; - // get all the current connections - Collection allConns = getAllConnections(); - log.debug("Checking {} connections", allConns.size()); - for (RTMPConnection conn : allConns) { - if (log.isTraceEnabled()) { - log.trace("{} session: {} state: {} keep-alive running: {}", new Object[] { conn.getClass().getSimpleName(), conn.getSessionId(), conn.getState().states[conn.getStateCode()], conn.running }); - log.trace("Decoder lock - permits: {} queue length: {}", conn.decoderLock.availablePermits(), conn.decoderLock.getQueueLength()); - log.trace("Encoder lock - permits: {} queue length: {}", conn.encoderLock.availablePermits(), conn.encoderLock.getQueueLength()); - log.trace("Client streams: {} used: {}", conn.getStreams().size(), conn.getUsedStreamCount()); - log.trace("Attributes: {}", conn.getAttributes()); - Iterator scopes = conn.getBasicScopes(); - while (scopes.hasNext()) { - IBasicScope scope = scopes.next(); - log.trace("Scope: {}", scope); - } - } - long ioTime = 0L; - IoSession session = null; - if (conn instanceof RTMPMinaConnection) { - session = ((RTMPMinaConnection) conn).getIoSession(); - ioTime = System.currentTimeMillis() - session.getLastIoTime(); - if (log.isTraceEnabled()) { - log.trace("Session - write queue: {} last io time: {} ms", session.getWriteRequestQueue().size(), ioTime); - log.trace("Managed session count: {}", session.getService().getManagedSessionCount()); - } - } else if (conn instanceof RTMPTConnection) { - ioTime = System.currentTimeMillis() - ((RTMPTConnection) conn).getLastDataReceived(); - } - // if exceeds max inactivity kill and clean up - if (ioTime >= conn.maxInactivity) { - log.warn("Connection {} has exceeded the max inactivity threshold", conn.getSessionId()); - if (session != null) { - if (log.isDebugEnabled()) { - log.debug("Prepared to clear write queue, if session is connected: {}; closing? {}", session.isConnected(), session.isClosing()); - } - if (session.isConnected()) { - // clear the write queue - session.getWriteRequestQueue().clear(session); - } - } - // call onInactive on the connection, this should cleanly close everything out - conn.onInactive(); - if (!conn.isClosed()) { - log.debug("Connection {} is not closed", conn.getSessionId()); - } - closedConnections++; - } - } - // if there is more than one connection that needed to be closed, request a GC to clean up memory. - if (closedConnections > 0) { - System.gc(); - } - } - }, 7000, 30000, TimeUnit.MILLISECONDS); - } - - public static IConnectionManager getInstance() { - if (instance == null) { - log.trace("Connection manager instance does not exist"); - if (applicationContext != null && applicationContext.containsBean("rtmpConnManager")) { - log.trace("Connection manager bean exists"); - instance = (RTMPConnManager) applicationContext.getBean("rtmpConnManager"); - } else { - log.trace("Connection manager bean doesnt exist, creating new instance"); - instance = new RTMPConnManager(); - } - } - return instance; - } - - /** {@inheritDoc} */ - public RTMPConnection createConnection(Class connCls) { - RTMPConnection conn = null; - if (RTMPConnection.class.isAssignableFrom(connCls)) { - try { - // create connection - conn = createConnectionInstance(connCls); - // add to local map - connMap.put(conn.getSessionId(), conn); - log.trace("Connections: {}", conns.incrementAndGet()); - // set the scheduler - if (applicationContext.containsBean("rtmpScheduler") && conn.getScheduler() == null) { - conn.setScheduler((ThreadPoolTaskScheduler) applicationContext.getBean("rtmpScheduler")); - } - log.trace("Connection created: {}", conn); - // start the wait for handshake - conn.startWaitForHandshake(); - } catch (Exception ex) { - log.warn("Exception creating connection", ex); - } - } - return conn; - } - - /** {@inheritDoc} */ - public RTMPConnection createConnection(Class connCls, String sessionId) { - throw new UnsupportedOperationException("Not implemented"); - } - - /** - * Adds a connection. - * - * @param conn - */ - public void setConnection(RTMPConnection conn) { - log.trace("Adding connection: {}", conn); - int id = conn.getId(); - if (id == -1) { - log.debug("Connection has unsupported id, using session id hash"); - id = conn.getSessionId().hashCode(); - } - log.debug("Connection id: {} session id hash: {}", conn.getId(), conn.getSessionId().hashCode()); - if (debug) { - log.info("Connection count (map): {}", connMap.size()); - try { - RTMPMinaTransportMXBean proxy = JMX.newMXBeanProxy(ManagementFactory.getPlatformMBeanServer(), new ObjectName("org.red5.server:type=RTMPMinaTransport"), - RTMPMinaTransportMXBean.class, true); - if (proxy != null) { - log.info("{}", proxy.getStatistics()); - } - } catch (Exception e) { - log.warn("Error on jmx lookup", e); - } - } - } - - /** - * Returns a connection for a given client id. - * - * @param clientId - * @return connection if found and null otherwise - */ - public RTMPConnection getConnection(int clientId) { - log.trace("Getting connection by client id: {}", clientId); - for (RTMPConnection conn : connMap.values()) { - if (conn.getId() == clientId) { - return connMap.get(conn.getSessionId()); - } - } - return null; - } - - /** - * Returns a connection for a given session id. - * - * @param sessionId - * @return connection if found and null otherwise - */ - public RTMPConnection getConnectionBySessionId(String sessionId) { - log.trace("Getting connection by session id: {}", sessionId); - if (connMap.containsKey(sessionId)) { - return connMap.get(sessionId); - } else { - log.warn("Connection not found for {}", sessionId); - if (log.isTraceEnabled()) { - log.trace("Connections ({}) {}", connMap.size(), connMap.values()); - } - } - return null; - } - - /** {@inheritDoc} */ - public RTMPConnection removeConnection(int clientId) { - log.trace("Removing connection with id: {}", clientId); - // remove from map - for (RTMPConnection conn : connMap.values()) { - if (conn.getId() == clientId) { - // remove the conn - return removeConnection(conn.getSessionId()); - } - } - log.warn("Connection was not removed by id: {}", clientId); - return null; - } - - /** {@inheritDoc} */ - public RTMPConnection removeConnection(String sessionId) { - log.trace("Removing connection with session id: {}", sessionId); - if (log.isTraceEnabled()) { - log.trace("Connections ({}) at pre-remove: {}", connMap.size(), connMap.values()); - } - // remove from map - RTMPConnection conn = connMap.remove(sessionId); - if (conn != null) { - log.trace("Connections: {}", conns.decrementAndGet()); - Red5.setConnectionLocal(null); - } - return conn; - } - - /** {@inheritDoc} */ - public Collection getAllConnections() { - ArrayList list = new ArrayList(connMap.size()); - list.addAll(connMap.values()); - return list; - } - - /** {@inheritDoc} */ - public Collection removeConnections() { - ArrayList list = new ArrayList(connMap.size()); - list.addAll(connMap.values()); - connMap.clear(); - conns.set(0); - return list; - } - - /** - * Creates a connection instance based on the supplied type. - * - * @param cls - * @return connection - * @throws Exception - */ - public RTMPConnection createConnectionInstance(Class cls) throws Exception { - RTMPConnection conn = null; - if (cls == RTMPMinaConnection.class) { - conn = (RTMPMinaConnection) applicationContext.getBean(RTMPMinaConnection.class); - } else if (cls == RTMPTConnection.class) { - conn = (RTMPTConnection) applicationContext.getBean(RTMPTConnection.class); - } else { - conn = (RTMPConnection) cls.newInstance(); - } - return conn; - } - - /** - * @param debug the debug to set - */ - public void setDebug(boolean debug) { - this.debug = debug; - } - - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - RTMPConnManager.applicationContext = applicationContext; - } - - public void destroy() throws Exception { - executor.shutdownNow(); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/RTMPConnection.java b/src/main/java/org/red5/server/net/rtmp/RTMPConnection.java deleted file mode 100755 index 8723b6f7b..000000000 --- a/src/main/java/org/red5/server/net/rtmp/RTMPConnection.java +++ /dev/null @@ -1,1618 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import java.beans.ConstructorProperties; -import java.util.BitSet; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.Semaphore; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.BaseConnection; -import org.red5.server.api.Red5; -import org.red5.server.api.event.IEvent; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.service.IPendingServiceCall; -import org.red5.server.api.service.IPendingServiceCallback; -import org.red5.server.api.service.IServiceCall; -import org.red5.server.api.service.IServiceCapableConnection; -import org.red5.server.api.stream.IClientBroadcastStream; -import org.red5.server.api.stream.IClientStream; -import org.red5.server.api.stream.IPlaylistSubscriberStream; -import org.red5.server.api.stream.ISingleItemSubscriberStream; -import org.red5.server.api.stream.IStreamCapableConnection; -import org.red5.server.api.stream.IStreamService; -import org.red5.server.exception.ClientRejectedException; -import org.red5.server.net.protocol.RTMPDecodeState; -import org.red5.server.net.rtmp.codec.RTMP; -import org.red5.server.net.rtmp.event.BytesRead; -import org.red5.server.net.rtmp.event.ClientBW; -import org.red5.server.net.rtmp.event.ClientInvokeEvent; -import org.red5.server.net.rtmp.event.ClientNotifyEvent; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.Ping; -import org.red5.server.net.rtmp.event.ServerBW; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.net.rtmp.message.Packet; -import org.red5.server.net.rtmp.status.Status; -import org.red5.server.service.Call; -import org.red5.server.service.PendingCall; -import org.red5.server.so.FlexSharedObjectMessage; -import org.red5.server.so.ISharedObjectEvent; -import org.red5.server.so.SharedObjectMessage; -import org.red5.server.stream.ClientBroadcastStream; -import org.red5.server.stream.OutputStream; -import org.red5.server.stream.PlaylistSubscriberStream; -import org.red5.server.stream.SingleItemSubscriberStream; -import org.red5.server.stream.StreamService; -import org.red5.server.util.ScopeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.task.TaskRejectedException; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; -import org.springframework.util.concurrent.ListenableFuture; -import org.springframework.util.concurrent.ListenableFutureCallback; -import org.springframework.util.concurrent.ListenableFutureTask; - -/** - * RTMP connection. Stores information about client streams, data transfer channels, pending RPC calls, bandwidth configuration, - * AMF encoding type (AMF0/AMF3), connection state (is alive, last ping time and ping result) and session. - */ -public abstract class RTMPConnection extends BaseConnection implements IStreamCapableConnection, IServiceCapableConnection { - - private static Logger log = LoggerFactory.getLogger(RTMPConnection.class); - - public static final String RTMP_SESSION_ID = "rtmp.sessionid"; - - public static final String RTMP_HANDSHAKE = "rtmp.handshake"; - - /** - * Marker byte for standard or non-encrypted RTMP data. - */ - public static final byte RTMP_NON_ENCRYPTED = (byte) 0x03; - - /** - * Marker byte for encrypted RTMP data. - */ - public static final byte RTMP_ENCRYPTED = (byte) 0x06; - - /** - * Cipher for RTMPE input - */ - public static final String RTMPE_CIPHER_IN = "rtmpe.cipher.in"; - - /** - * Cipher for RTMPE output - */ - public static final String RTMPE_CIPHER_OUT = "rtmpe.cipher.out"; - - /** - * Connection channels - * - * @see org.red5.server.net.rtmp.Channel - */ - private ConcurrentMap channels = new ConcurrentHashMap(3, 0.9f, 1); - - /** - * Client streams - * - * @see org.red5.server.api.stream.IClientStream - */ - private ConcurrentMap streams = new ConcurrentHashMap(1, 0.9f, 1); - - /** - * Reserved stream ids. Stream id's directly relate to individual NetStream instances. - */ - private volatile BitSet reservedStreams = new BitSet(); - - /** - * Transaction identifier for remote commands. - */ - private AtomicInteger transactionId = new AtomicInteger(1); - - /** - * Hash map that stores pending calls and ids as pairs. - */ - private ConcurrentMap pendingCalls = new ConcurrentHashMap(3, 0.75f, 1); - - /** - * Deferred results set. - * - * @see org.red5.server.net.rtmp.DeferredResult - */ - private CopyOnWriteArraySet deferredResults = new CopyOnWriteArraySet(); - - /** - * Last ping round trip time - */ - private AtomicInteger lastPingRoundTripTime = new AtomicInteger(-1); - - /** - * Timestamp when last ping command was sent. - */ - private AtomicLong lastPingSentOn = new AtomicLong(0); - - /** - * Timestamp when last ping result was received. - */ - private AtomicLong lastPongReceivedOn = new AtomicLong(0); - - /** - * RTMP events handler - */ - protected IRTMPHandler handler; - - /** - * Ping interval in ms to detect dead clients. - */ - private volatile int pingInterval = 5000; - - /** - * Maximum time in ms after which a client is disconnected because of inactivity. - */ - protected volatile int maxInactivity = 60000; - - /** - * Data read interval - */ - protected long bytesReadInterval = 1024 * 1024; - - /** - * Number of bytes to read next. - */ - protected long nextBytesRead = 1024 * 1024; - - /** - * Number of bytes the client reported to have received. - */ - private AtomicLong clientBytesRead = new AtomicLong(0L); - - /** - * Map for pending video packets and stream IDs. - */ - private ConcurrentMap pendingVideos = new ConcurrentHashMap(1, 0.9f, 1); - - /** - * Number of (NetStream) streams used. - */ - private AtomicInteger usedStreams = new AtomicInteger(0); - - /** - * Remembered stream buffer durations. - */ - private ConcurrentMap streamBuffers = new ConcurrentHashMap(1, 0.9f, 1); - - /** - * Maximum time in milliseconds to wait for a valid handshake. - */ - private int maxHandshakeTimeout = 5000; - - /** - * Maximum time in milliseconds allowed to process received message - */ - protected long maxHandlingTimeout = 500L; - - /** - * Bandwidth limit type / enforcement. (0=hard,1=soft,2=dynamic) - */ - protected int limitType = 0; - - /** - * Protocol state - */ - protected RTMP state = new RTMP(); - - // protection for the decoder when using multiple threads per connection - protected Semaphore decoderLock = new Semaphore(1, true); - - // protection for the encoder when using multiple threads per connection - protected Semaphore encoderLock = new Semaphore(1, true); - - // keeps track of the decode state - protected ThreadLocal decoderState = new ThreadLocal() { - - @Override - protected RTMPDecodeState initialValue() { - return new RTMPDecodeState(getSessionId()); - } - - }; - - /** - * Scheduling service - */ - protected ThreadPoolTaskScheduler scheduler; - - /** - * Thread pool for message handling. - */ - protected ThreadPoolTaskExecutor executor; - - /** - * Keep-alive worker flag - */ - protected final AtomicBoolean running; - - /** - * Timestamp generator - */ - private final AtomicInteger timer = new AtomicInteger(0); - - /** - * Closing flag - */ - private final AtomicBoolean closing = new AtomicBoolean(false); - - /** - * Packet sequence number - * */ - private AtomicLong packetSequence = new AtomicLong(); - - /** - * Specify the size of queue that will trigger audio packet dropping, disabled if it's 0 - * */ - private Integer executorQueueSizeToDropAudioPackets = 0; - - /** - * Keep track of current queue size - * */ - private AtomicInteger currentQueueSize = new AtomicInteger(); - - - /** - * Creates anonymous RTMP connection without scope. - * - * @param type Connection type - */ - @ConstructorProperties({ "type" }) - public RTMPConnection(String type) { - // We start with an anonymous connection without a scope. - // These parameters will be set during the call of "connect" later. - super(type); - // set running flag - running = new AtomicBoolean(false); - } - - public int getId() { - // handle the fact that a client id is a String - return client != null ? client.getId().hashCode() : -1; - } - - @Deprecated - public void setId(int clientId) { - log.warn("Setting of a client id is deprecated, use IClient to manipulate the id", new Exception("RTMPConnection.setId is deprecated")); - } - - public void setHandler(IRTMPHandler handler) { - this.handler = handler; - } - - public RTMP getState() { - return state; - } - - public byte getStateCode() { - return state.getState(); - } - - public void setStateCode(byte code) { - if (log.isTraceEnabled()) - log.trace("setStateCode: {} - {}", code, state.states[code]); - state.setState(code); - } - - /** - * @return the decoderLock - */ - public Semaphore getDecoderLock() { - return decoderLock; - } - - /** - * @return the decoderLock - */ - public Semaphore getEncoderLock() { - return encoderLock; - } - - /** - * @return the decoderState - */ - public RTMPDecodeState getDecoderState() { - return decoderState.get(); - } - - /** {@inheritDoc} */ - public void setBandwidth(int mbits) { - // tell the flash player how fast we want data and how fast we shall send it - getChannel(2).write(new ServerBW(mbits)); - // second param is the limit type (0=hard,1=soft,2=dynamic) - getChannel(2).write(new ClientBW(mbits, (byte) limitType)); - } - - /** - * Returns a usable timestamp for written packets. - * - * @return timestamp - */ - public int getTimer() { - return timer.incrementAndGet(); - } - - /** - * Opens the connection. - */ - public void open() { - // add the session id to the prefix - executor.setThreadNamePrefix(String.format("RTMPConnectionExecutor#%s-", sessionId)); - //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); - //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); - //executor.setAllowCoreThreadTimeOut(true); - executor.setDaemon(true); - executor.setWaitForTasksToCompleteOnShutdown(true); - if (log.isTraceEnabled()) { - // dump memory stats - log.trace("Memory at open - free: {}K total: {}K", Runtime.getRuntime().freeMemory() / 1024, Runtime.getRuntime().totalMemory() / 1024); - } - } - - @Override - public boolean connect(IScope newScope, Object[] params) { - if (log.isDebugEnabled()) - log.debug("Connect scope: {}", newScope); - try { - boolean success = super.connect(newScope, params); - if (success) { - // once the handshake has completed, start needed jobs start the ping / pong keep-alive - startRoundTripMeasurement(); - } else { - if (log.isDebugEnabled()) - log.debug("Connect failed"); - } - return success; - } catch (ClientRejectedException e) { - String reason = (String) e.getReason(); - log.info("Client rejected, reason: " + ((reason != null) ? reason : "None")); - throw e; - } - } - - /** - * Start waiting for a valid handshake. - */ - public void startWaitForHandshake() { - if (log.isDebugEnabled()) - log.debug("startWaitForHandshake - {}", sessionId); - // start the handshake waiter - scheduler.execute(new WaitForHandshakeTask()); - } - - /** - * Starts measurement. - */ - public void startRoundTripMeasurement() { - if (scheduler != null) { - if (pingInterval > 0) { - if (log.isDebugEnabled()) - log.debug("startRoundTripMeasurement - {}", sessionId); - try { - scheduler.scheduleAtFixedRate(new KeepAliveTask(), pingInterval); - if (log.isDebugEnabled()) - log.debug("Keep alive scheduled for: {}", sessionId); - } catch (Exception e) { - log.error("Error creating keep alive job", e); - } - } - } else { - log.warn("startRoundTripMeasurement cannot be executed due to missing scheduler. This can happen if a connection drops before handshake is complete"); - } - } - - /** - * Initialize connection. - * - * @param host Connection host - * @param path Connection path - * @param params Params passed from client - */ - public void setup(String host, String path, Map params) { - this.host = host; - this.path = path; - this.params = params; - if (Integer.valueOf(3).equals(params.get("objectEncoding"))) { - if (log.isDebugEnabled()) - log.debug("Setting object encoding to AMF3"); - state.setEncoding(Encoding.AMF3); - } - } - - /** - * Return AMF protocol encoding used by this connection. - * - * @return AMF encoding used by connection - */ - public Encoding getEncoding() { - return state.getEncoding(); - } - - /** - * Getter for next available channel id. - * - * @return Next available channel id - */ - public int getNextAvailableChannelId() { - int result = 4; - while (isChannelUsed(result)) { - result++; - } - return result; - } - - /** - * Checks whether channel is used. - * - * @param channelId Channel id - * @return true if channel is in use, false - * otherwise - */ - public boolean isChannelUsed(int channelId) { - return channels.get(channelId) != null; - } - - /** - * Return channel by id. - * - * @param channelId Channel id - * @return Channel by id - */ - public Channel getChannel(int channelId) { - if (channels != null) { - Channel channel = channels.putIfAbsent(channelId, new Channel(this, channelId)); - if (channel == null) { - channel = channels.get(channelId); - } - return channel; - } else { - return new Channel(null, channelId); - } - } - - /** - * Closes channel. - * - * @param channelId Channel id - */ - public void closeChannel(int channelId) { - Channel chan = channels.remove(channelId); - if (log.isDebugEnabled()) - log.debug("Closing / removing channel: {}", chan); - chan = null; - } - - /** - * Getter for client streams. - * - * @return Client streams as array - */ - protected Collection getStreams() { - return streams.values(); - } - - /** {@inheritDoc} */ - public int reserveStreamId() { - int result = -1; - for (int i = 0; true; i++) { - if (!reservedStreams.get(i)) { - reservedStreams.set(i); - result = i; - break; - } - } - return result + 1; - } - - /** {@inheritDoc} */ - public int reserveStreamId(int id) { - int result = -1; - if (!reservedStreams.get(id - 1)) { - reservedStreams.set(id - 1); - result = id - 1; - } else { - result = reserveStreamId(); - } - return result; - } - - /** - * Returns whether or not a given stream id is valid. - * - * @param streamId - * @return true if its valid, false if its invalid - */ - public boolean isValidStreamId(int streamId) { - int index = streamId - 1; - if (index < 0 || !reservedStreams.get(index)) { - // stream id has not been reserved before - return false; - } - if (streams.get(streamId - 1) != null) { - // another stream already exists with this id - return false; - } - return true; - } - - /** - * Returns whether or not the connection has been idle for a maximum period. - * - * @return true if max idle period has been exceeded, false otherwise - */ - public boolean isIdle() { - long lastPingTime = lastPingSentOn.get(); - long lastPongTime = lastPongReceivedOn.get(); - boolean idle = (lastPongTime > 0 && (lastPingTime - lastPongTime > maxInactivity)); - if (log.isTraceEnabled()) - log.trace("Connection {} {} idle", getSessionId(), (idle ? "is" : "is not")); - return idle; - } - - /** - * Returns whether or not the connection is disconnected. - * - * @return true if connection state is RTMP.STATE_DISCONNECTED, false otherwise - */ - public boolean isDisconnected() { - return state.getState() == RTMP.STATE_DISCONNECTED; - } - - /** - * Creates output stream object from stream id. Output stream consists of audio, data and video channels. - * - * @see org.red5.server.stream.OutputStream - * - * @param streamId Stream id - * @return Output stream object - */ - public OutputStream createOutputStream(int streamId) { - int channelId = (4 + ((streamId - 1) * 5)); - if (log.isDebugEnabled()) - log.debug("Channel id range start: {}", channelId); - final Channel data = getChannel(channelId++); - final Channel video = getChannel(channelId++); - final Channel audio = getChannel(channelId++); - return new OutputStream(video, audio, data); - } - - /** {@inheritDoc} */ - public IClientBroadcastStream newBroadcastStream(int streamId) { - if (isValidStreamId(streamId)) { - // get ClientBroadcastStream defined as a prototype in red5-common.xml - ClientBroadcastStream cbs = (ClientBroadcastStream) scope.getContext().getBean("clientBroadcastStream"); - Integer buffer = streamBuffers.get(streamId - 1); - if (buffer != null) { - cbs.setClientBufferDuration(buffer); - } - cbs.setStreamId(streamId); - cbs.setConnection(this); - cbs.setName(createStreamName()); - cbs.setScope(this.getScope()); - - registerStream(cbs); - usedStreams.incrementAndGet(); - return cbs; - } - return null; - } - - /** {@inheritDoc} */ - public ISingleItemSubscriberStream newSingleItemSubscriberStream(int streamId) { - if (isValidStreamId(streamId)) { - // get SingleItemSubscriberStream defined as a prototype in red5-common.xml - SingleItemSubscriberStream siss = (SingleItemSubscriberStream) scope.getContext().getBean("singleItemSubscriberStream"); - Integer buffer = streamBuffers.get(streamId - 1); - if (buffer != null) { - siss.setClientBufferDuration(buffer); - } - siss.setName(createStreamName()); - siss.setConnection(this); - siss.setScope(this.getScope()); - siss.setStreamId(streamId); - registerStream(siss); - usedStreams.incrementAndGet(); - return siss; - } - return null; - } - - /** {@inheritDoc} */ - public IPlaylistSubscriberStream newPlaylistSubscriberStream(int streamId) { - if (isValidStreamId(streamId)) { - // get PlaylistSubscriberStream defined as a prototype in red5-common.xml - PlaylistSubscriberStream pss = (PlaylistSubscriberStream) scope.getContext().getBean("playlistSubscriberStream"); - Integer buffer = streamBuffers.get(streamId - 1); - if (buffer != null) { - pss.setClientBufferDuration(buffer); - } - pss.setName(createStreamName()); - pss.setConnection(this); - pss.setScope(this.getScope()); - pss.setStreamId(streamId); - registerStream(pss); - usedStreams.incrementAndGet(); - return pss; - } - return null; - } - - public void addClientStream(IClientStream stream) { - int streamIndex = stream.getStreamId() - 1; - if (!reservedStreams.get(streamIndex)) { - reservedStreams.set(streamIndex); - streams.put(streamIndex, stream); - usedStreams.incrementAndGet(); - } - } - - public void removeClientStream(int streamId) { - unreserveStreamId(streamId); - } - - /** - * Getter for used stream count. - * - * @return Value for property 'usedStreamCount'. - */ - protected int getUsedStreamCount() { - return usedStreams.get(); - } - - /** {@inheritDoc} */ - public IClientStream getStreamById(int id) { - if (id <= 0) { - return null; - } - return streams.get(id - 1); - } - - /** - * Return stream id for given channel id. - * - * @param channelId Channel id - * @return ID of stream that channel belongs to - */ - public int getStreamIdForChannel(int channelId) { - if (channelId < 4) { - return 0; - } - return ((channelId - 4) / 5) + 1; - } - - /** - * Return stream by given channel id. - * - * @param channelId Channel id - * @return Stream that channel belongs to - */ - public IClientStream getStreamByChannelId(int channelId) { - if (channelId < 4) { - return null; - } - return streams.get(getStreamIdForChannel(channelId) - 1); - } - - /** - * Store a stream in the connection. - * - * @param stream - */ - private void registerStream(IClientStream stream) { - streams.put(stream.getStreamId() - 1, stream); - } - - /** - * Remove a stream from the connection. - * - * @param stream - */ - @SuppressWarnings("unused") - private void unregisterStream(IClientStream stream) { - streams.remove(stream.getStreamId()); - } - - /** {@inheritDoc} */ - @Override - public void close() { - if (closing.compareAndSet(false, true)) { - if (log.isDebugEnabled()) - log.debug("close: {}", sessionId); - // update our state - if (state != null) { - final byte s = getStateCode(); - switch (s) { - case RTMP.STATE_DISCONNECTED: - if (log.isDebugEnabled()) - log.debug("Already disconnected"); - return; - default: - if (log.isDebugEnabled()) - log.debug("State: {}", state.states[s]); - state.setState(RTMP.STATE_DISCONNECTING); - } - } - Red5.setConnectionLocal(this); - IStreamService streamService = (IStreamService) ScopeUtils.getScopeService(scope, IStreamService.class, StreamService.class); - if (streamService != null) { - for (Map.Entry entry : streams.entrySet()) { - IClientStream stream = entry.getValue(); - if (stream != null) { - if (log.isDebugEnabled()) - log.debug("Closing stream: {}", stream.getStreamId()); - streamService.deleteStream(this, stream.getStreamId()); - usedStreams.decrementAndGet(); - } - } - } else { - if (log.isDebugEnabled()) - log.debug("Stream service was not found for scope: {}", (scope != null ? scope.getName() : "null or non-existant")); - } - // close the base connection - disconnect scopes and unregister client - super.close(); - // kill all the collections etc - if (channels != null) { - channels.clear(); - } else { - if (log.isTraceEnabled()) - log.trace("Channels collection was null"); - } - if (streams != null) { - streams.clear(); - } else { - if (log.isTraceEnabled()) - log.trace("Streams collection was null"); - } - if (pendingCalls != null) { - pendingCalls.clear(); - } else { - if (log.isTraceEnabled()) - log.trace("PendingCalls collection was null"); - } - if (deferredResults != null) { - deferredResults.clear(); - } else { - if (log.isTraceEnabled()) - log.trace("DeferredResults collection was null"); - } - if (pendingVideos != null) { - pendingVideos.clear(); - } else { - if (log.isTraceEnabled()) - log.trace("PendingVideos collection was null"); - } - if (streamBuffers != null) { - streamBuffers.clear(); - } else { - if (log.isTraceEnabled()) - log.trace("StreamBuffers collection was null"); - } - if (scheduler != null) { - if (log.isDebugEnabled()) - log.debug("Shutting down scheduler"); - try { - ScheduledExecutorService exe = scheduler.getScheduledExecutor(); - List runables = exe.shutdownNow(); - if (log.isDebugEnabled()) - log.debug("Scheduler - shutdown: {} queued: {}", exe.isShutdown(), runables.size()); - if (scheduler != null) { - scheduler.shutdown(); - scheduler = null; - } else { - return; - } - } catch (NullPointerException e) { - // this can happen in a multithreaded env, where close has been called from more than one spot - if (log.isDebugEnabled()) { - log.warn("Exception during scheduler shutdown", e); - } - } catch (Exception e) { - log.warn("Exception during scheduler shutdown", e); - } - } - if (executor != null) { - if (log.isDebugEnabled()) - log.debug("Shutting down executor"); - try { - ThreadPoolExecutor exe = executor.getThreadPoolExecutor(); - List runables = exe.shutdownNow(); - if (log.isDebugEnabled()) - log.debug("Executor - shutdown: {} queued: {}", exe.isShutdown(), runables.size()); - if (executor != null) { - executor.shutdown(); - executor = null; - } else { - return; - } - } catch (NullPointerException e) { - // this can happen in a multithreaded env, where close has been called from more than one spot - if (log.isDebugEnabled()) { - log.warn("Exception during executor shutdown", e); - } - } catch (Exception e) { - log.warn("Exception during executor shutdown", e); - } - } - // drain permits - decoderLock.drainPermits(); - encoderLock.drainPermits(); - if (log.isTraceEnabled()) { - // dump memory stats - if (log.isTraceEnabled()) - log.trace("Memory at close - free: {}K total: {}K", Runtime.getRuntime().freeMemory() / 1024, Runtime.getRuntime().totalMemory() / 1024); - } - } else { - if (log.isDebugEnabled()) - log.debug("Already closing.."); - } - } - - /** - * Dispatches event - * @param event Event - */ - @Override - public void dispatchEvent(IEvent event) { - if (log.isDebugEnabled()) - log.debug("Event notify: {}", event); - // determine if its an outgoing invoke or notify - switch (event.getType()) { - case CLIENT_INVOKE: - ClientInvokeEvent cie = (ClientInvokeEvent) event; - invoke(cie.getMethod(), cie.getParams(), cie.getCallback()); - break; - case CLIENT_NOTIFY: - ClientNotifyEvent cne = (ClientNotifyEvent) event; - notify(cne.getMethod(), cne.getParams()); - break; - default: - log.warn("Unhandled event: {}", event); - } - } - - /** - * When the connection has been closed, notify any remaining pending service calls that they have failed because - * the connection is broken. Implementors of IPendingServiceCallback may only deduce from this notification that - * it was not possible to read a result for this service call. It is possible that (1) the service call was never - * written to the service, or (2) the service call was written to the service and although the remote method was - * invoked, the connection failed before the result could be read, or (3) although the remote method was invoked - * on the service, the service implementor detected the failure of the connection and performed only partial - * processing. The caller only knows that it cannot be confirmed that the callee has invoked the service call - * and returned a result. - */ - public void sendPendingServiceCallsCloseError() { - if (pendingCalls != null && !pendingCalls.isEmpty()) { - if (log.isDebugEnabled()) - log.debug("Connection calls pending: {}", pendingCalls.size()); - for (IPendingServiceCall call : pendingCalls.values()) { - call.setStatus(Call.STATUS_NOT_CONNECTED); - for (IPendingServiceCallback callback : call.getCallbacks()) { - callback.resultReceived(call); - } - } - } - } - - /** {@inheritDoc} */ - public void unreserveStreamId(int streamId) { - deleteStreamById(streamId); - if (streamId > 0) { - reservedStreams.clear(streamId - 1); - } - } - - /** {@inheritDoc} */ - public void deleteStreamById(int streamId) { - if (streamId > 0) { - if (streams.get(streamId - 1) != null) { - pendingVideos.remove(streamId); - usedStreams.decrementAndGet(); - streams.remove(streamId - 1); - streamBuffers.remove(streamId - 1); - } - } - } - - /** - * Handler for ping event. - * - * @param ping Ping event context - */ - public void ping(Ping ping) { - getChannel(2).write(ping); - } - - /** - * Write packet. - * - * @param out Packet - */ - public abstract void write(Packet out); - - /** - * Write raw byte buffer. - * - * @param out IoBuffer - */ - public abstract void writeRaw(IoBuffer out); - - /** - * Update number of bytes to read next value. - */ - protected void updateBytesRead() { - if (log.isTraceEnabled()) - log.trace("updateBytesRead"); - long bytesRead = getReadBytes(); - if (bytesRead >= nextBytesRead) { - BytesRead sbr = new BytesRead((int) (bytesRead % Integer.MAX_VALUE)); - getChannel(2).write(sbr); - nextBytesRead += bytesReadInterval; - } - } - - /** - * Read number of received bytes. - * - * @param bytes Number of bytes - */ - public void receivedBytesRead(int bytes) { - if (log.isDebugEnabled()) - log.debug("Client received {} bytes, written {} bytes, {} messages pending", new Object[] { bytes, getWrittenBytes(), getPendingMessages() }); - clientBytesRead.addAndGet(bytes); - } - - /** - * Get number of bytes the client reported to have received. - * - * @return Number of bytes - */ - public long getClientBytesRead() { - return clientBytesRead.get(); - } - - /** {@inheritDoc} */ - public void invoke(IServiceCall call) { - invoke(call, 3); - } - - /** - * Generate next invoke id. - * - * @return Next invoke id for RPC - */ - public int getTransactionId() { - return transactionId.incrementAndGet(); - } - - /** - * Register pending call (remote function call that is yet to finish). - * - * @param invokeId Deferred operation id - * @param call Call service - */ - public void registerPendingCall(int invokeId, IPendingServiceCall call) { - pendingCalls.put(invokeId, call); - } - - /** {@inheritDoc} */ - public void invoke(IServiceCall call, int channel) { - // We need to use Invoke for all calls to the client - Invoke invoke = new Invoke(); - invoke.setCall(call); - invoke.setTransactionId(getTransactionId()); - if (call instanceof IPendingServiceCall) { - registerPendingCall(invoke.getTransactionId(), (IPendingServiceCall) call); - } - getChannel(channel).write(invoke); - } - - /** {@inheritDoc} */ - public void invoke(String method) { - invoke(method, null, null); - } - - /** {@inheritDoc} */ - public void invoke(String method, Object[] params) { - invoke(method, params, null); - } - - /** {@inheritDoc} */ - public void invoke(String method, IPendingServiceCallback callback) { - invoke(method, null, callback); - } - - /** {@inheritDoc} */ - public void invoke(String method, Object[] params, IPendingServiceCallback callback) { - IPendingServiceCall call = new PendingCall(method, params); - if (callback != null) { - call.registerCallback(callback); - } - invoke(call); - } - - /** {@inheritDoc} */ - public void notify(IServiceCall call) { - notify(call, 3); - } - - /** {@inheritDoc} */ - public void notify(IServiceCall call, int channel) { - Notify notify = new Notify(); - notify.setCall(call); - getChannel(channel).write(notify); - } - - /** {@inheritDoc} */ - public void notify(String method) { - notify(method, null); - } - - /** {@inheritDoc} */ - public void notify(String method, Object[] params) { - IServiceCall call = new Call(method, params); - notify(call); - } - - /** {@inheritDoc} */ - public void status(Status status) { - status(status, 3); - } - - /** {@inheritDoc} */ - public void status(Status status, int channel) { - if (status != null) { - getChannel(channel).sendStatus(status); - } - } - - /** {@inheritDoc} */ - @Override - public long getReadBytes() { - return 0; - } - - /** {@inheritDoc} */ - @Override - public long getWrittenBytes() { - return 0; - } - - /** - * Get pending call service by id. - * - * @param invokeId - * Pending call service id - * @return Pending call service object - */ - public IPendingServiceCall getPendingCall(int invokeId) { - return pendingCalls.get(invokeId); - } - - /** - * Retrieves and removes the pending call service by id. - * - * @param invokeId - * Pending call service id - * @return Pending call service object - */ - public IPendingServiceCall retrievePendingCall(int invokeId) { - return pendingCalls.remove(invokeId); - } - - /** - * Generates new stream name. - * - * @return New stream name - */ - protected String createStreamName() { - return UUID.randomUUID().toString(); - } - - /** - * Mark message as being written. - * - * @param message - * Message to mark - */ - protected void writingMessage(Packet message) { - if (message.getMessage() instanceof VideoData) { - int streamId = message.getHeader().getStreamId(); - final AtomicInteger value = new AtomicInteger(); - AtomicInteger old = pendingVideos.putIfAbsent(streamId, value); - if (old == null) { - old = value; - } - old.incrementAndGet(); - } - } - - /** - * Increases number of read messages by one. Updates number of bytes read. - */ - public void messageReceived() { - if (log.isTraceEnabled()) - log.trace("messageReceived"); - readMessages.incrementAndGet(); - // trigger generation of BytesRead messages - updateBytesRead(); - } - - private String getMessageType(Packet packet) { - final Header header = packet.getHeader(); - final byte headerDataType = header.getDataType(); - return messageTypeToName(headerDataType); - } - - public String messageTypeToName(byte headerDataType) { - switch (headerDataType) { - case Constants.TYPE_AGGREGATE: - return "TYPE_AGGREGATE"; - case Constants.TYPE_AUDIO_DATA: - return "TYPE_AUDIO_DATA"; - case Constants.TYPE_VIDEO_DATA: - return "TYPE_VIDEO_DATA"; - case Constants.TYPE_FLEX_SHARED_OBJECT: - return "TYPE_FLEX_SHARED_OBJECT"; - case Constants.TYPE_SHARED_OBJECT: - return "TYPE_SHARED_OBJECT"; - case Constants.TYPE_INVOKE: - return "TYPE_INVOKE"; - case Constants.TYPE_FLEX_MESSAGE: - return "TYPE_FLEX_MESSAGE"; - case Constants.TYPE_NOTIFY: - return "TYPE_NOTIFY"; - case Constants.TYPE_FLEX_STREAM_SEND: - return "TYPE_FLEX_STREAM_SEND"; - case Constants.TYPE_PING: - return "TYPE_PING"; - case Constants.TYPE_BYTES_READ: - return "TYPE_BYTES_READ"; - case Constants.TYPE_CHUNK_SIZE: - return "TYPE_CHUNK_SIZE"; - case Constants.TYPE_CLIENT_BANDWIDTH: - return "TYPE_CLIENT_BANDWIDTH"; - case Constants.TYPE_SERVER_BANDWIDTH: - return "TYPE_SERVER_BANDWIDTH"; - default: - return "UNKNOWN [" + headerDataType + "]"; - - } - } - - /** - * Handle the incoming message. - * - * @param message - */ - @SuppressWarnings("unchecked") - public void handleMessageReceived(Packet message) { - if (log.isTraceEnabled()) - log.trace("handleMessageReceived - {}", sessionId); - final byte dataType = message.getHeader().getDataType(); - // route these types outside the executor - switch(dataType) { - case Constants.TYPE_PING: - case Constants.TYPE_ABORT: - case Constants.TYPE_BYTES_READ: - case Constants.TYPE_CHUNK_SIZE: - case Constants.TYPE_CLIENT_BANDWIDTH: - case Constants.TYPE_SERVER_BANDWIDTH: - // pass message to the handler - try { - handler.messageReceived(this, message); - } catch (Exception e) { - log.error("Error processing received message {}", sessionId, e); - } - break; - default: - if (executor != null) { - try { - final long packetNumber = packetSequence.incrementAndGet(); - - if (executorQueueSizeToDropAudioPackets > 0 && currentQueueSize.get() >= executorQueueSizeToDropAudioPackets) { - if (message.getHeader().getDataType() == Constants.TYPE_AUDIO_DATA){ - /** - * There's a backlog of messages in the queue. Flash might have - * sent a burst of messages after a network congestion. - * Throw away packets that we are able to discard. - */ - log.info("Queue threshold reached. Discarding packet: session=[{}], msgType=[{}], packetNum=[{}]", getSessionId(), getMessageType(message), packetNumber); - return ; - } - } - ReceivedMessageTask task = new ReceivedMessageTask(sessionId, message, handler, this); - task.setMaxHandlingTimeout(maxHandlingTimeout); - packetSequence.incrementAndGet(); - final Packet sentMessage = message; - final Long startTime = System.nanoTime(); - ListenableFuture future = (ListenableFuture) executor.submitListenable(new ListenableFutureTask(task)); - currentQueueSize.incrementAndGet(); - future.addCallback(new ListenableFutureCallback() { - private int getProcessingTime() { - return (int) ((System.nanoTime() - startTime)/1000); - } - - public void onFailure(Throwable t) { - currentQueueSize.decrementAndGet(); - - if (log.isWarnEnabled()) - log.warn("onFailure - session: {}, msgtype: {}, processingTime: {}, packetNum: {}", sessionId, getMessageType(sentMessage), getProcessingTime(), packetNumber); - } - - public void onSuccess(Boolean success) { - currentQueueSize.decrementAndGet(); - if (log.isDebugEnabled()) - log.debug("onSuccess - session: {}, msgType: {}, processingTime: {}, packetNum: {}", sessionId, getMessageType(sentMessage), getProcessingTime(), packetNumber); - } - }); - } catch (TaskRejectedException tre) { - Throwable[] suppressed = tre.getSuppressed(); - for (Throwable t : suppressed) { - log.warn("Suppressed exception on {}", sessionId, t); - } - log.info("Rejected message: {} on {}", message, sessionId); - } catch (Exception e) { - log.info("Incoming message handling failed on session=[{}], messageType=[{}]", getSessionId(), message); - if (log.isDebugEnabled()) { - log.debug("Execution rejected on {} - {}", getSessionId(), state.states[getStateCode()]); - log.debug("Lock permits - decode: {} encode: {}", decoderLock.availablePermits(), encoderLock.availablePermits()); - } - } - } else { - log.warn("Executor is null on {} state: {}", getSessionId(), state.states[getStateCode()]); - } - } - } - - /** - * Mark message as sent. - * - * @param message - * Message to mark - */ - public void messageSent(Packet message) { - if (message.getMessage() instanceof VideoData) { - int streamId = message.getHeader().getStreamId(); - AtomicInteger pending = pendingVideos.get(streamId); - if (log.isTraceEnabled()) { - log.trace("Stream id: {} pending: {} total pending videos: {}", streamId, pending, pendingVideos.size()); - } - if (pending != null) { - pending.decrementAndGet(); - } - } - writtenMessages.incrementAndGet(); - } - - /** - * Increases number of dropped messages. - */ - protected void messageDropped() { - droppedMessages.incrementAndGet(); - } - - /** {@inheritDoc} */ - @Override - public long getPendingVideoMessages(int streamId) { - if (log.isTraceEnabled()) { - log.trace("Total pending videos: {}", pendingVideos.size()); - } - AtomicInteger count = pendingVideos.get(streamId); - long result = (count != null ? count.intValue() - getUsedStreamCount() : 0); - return (result > 0 ? result : 0); - } - - /** - * Send a shared object message. - * - * @param name shared object name - * @param currentVersion the current version - * @param persistent - * @param events - */ - public void sendSharedObjectMessage(String name, int currentVersion, boolean persistent, ConcurrentLinkedQueue events) { - // create a new sync message for every client to avoid concurrent access through multiple threads - SharedObjectMessage syncMessage = state.getEncoding() == Encoding.AMF3 ? new FlexSharedObjectMessage(null, name, currentVersion, persistent) : new SharedObjectMessage( - null, name, currentVersion, persistent); - syncMessage.addEvents(events); - try { - // get the channel for so updates - Channel channel = getChannel((byte) 3); - if (log.isTraceEnabled()) - log.trace("Send to channel: {}", channel); - channel.write(syncMessage); - } catch (Exception e) { - log.warn("Exception sending shared object", e); - } - } - - /** {@inheritDoc} */ - public void ping() { - long newPingTime = System.currentTimeMillis(); - if (log.isDebugEnabled()) - log.debug("Send Ping: session=[{}], currentTime=[{}], lastPingTime=[{}]", new Object[] { getSessionId(), newPingTime, lastPingSentOn.get() }); - if (lastPingSentOn.get() == 0) { - lastPongReceivedOn.set(newPingTime); - } - Ping pingRequest = new Ping(); - pingRequest.setEventType(Ping.PING_CLIENT); - lastPingSentOn.set(newPingTime); - int now = (int) (newPingTime & 0xffffffff); - pingRequest.setValue2(now); - ping(pingRequest); - } - - /** - * Marks that ping back was received. - * - * @param pong - * Ping object - */ - public void pingReceived(Ping pong) { - long now = System.currentTimeMillis(); - long previousPingValue = (int) (lastPingSentOn.get() & 0xffffffff); - if (log.isDebugEnabled()) - log.debug("Pong Rx: session=[{}] at {} with value {}, previous received at {}", new Object[] { getSessionId(), now, pong.getValue2(), previousPingValue }); - if (pong.getValue2() == previousPingValue) { - lastPingRoundTripTime.set((int) (now & 0xffffffff) - pong.getValue2()); - if (log.isDebugEnabled()) - log.debug("Ping response session=[{}], RTT=[{} ms]", new Object[] { getSessionId(), lastPingRoundTripTime.get() }); - } else { - int pingRtt = (int) (now & 0xffffffff) - pong.getValue2(); - log.info("Pong delayed: session=[{}], ping response took [{} ms] to arrive. Connection may be congested.", new Object[] { getSessionId(), pingRtt }); - } - lastPongReceivedOn.set(now); - } - - /** {@inheritDoc} */ - public int getLastPingTime() { - return lastPingRoundTripTime.get(); - } - - /** - * Setter for ping interval. - * - * @param pingInterval Interval in ms to ping clients. Set to 0 to - * disable ghost detection code. - */ - public void setPingInterval(int pingInterval) { - this.pingInterval = pingInterval; - } - - /** - * Setter for maximum inactivity. - * - * @param maxInactivity Maximum time in ms after which a client is disconnected in - * case of inactivity. - */ - public void setMaxInactivity(int maxInactivity) { - this.maxInactivity = maxInactivity; - } - - /** - * Inactive state event handler. - */ - protected abstract void onInactive(); - - /** - * Sets the scheduler. - * - * @param scheduler scheduling service / thread executor - */ - public void setScheduler(ThreadPoolTaskScheduler scheduler) { - this.scheduler = scheduler; - // set the prefix - this.scheduler.setThreadNamePrefix(String.format("RTMPConnectionExecutor#%d", System.currentTimeMillis())); - this.scheduler.setDaemon(true); - this.scheduler.setWaitForTasksToCompleteOnShutdown(true); - } - - /** - * @return the scheduler - */ - public ThreadPoolTaskScheduler getScheduler() { - return scheduler; - } - - public ThreadPoolTaskExecutor getExecutor() { - return executor; - } - - public void setExecutor(ThreadPoolTaskExecutor executor) { - this.executor = executor; - } - - /** - * Registers deferred result. - * - * @param result Result to register - */ - public void registerDeferredResult(DeferredResult result) { - deferredResults.add(result); - } - - /** - * Unregister deferred result - * - * @param result - * Result to unregister - */ - public void unregisterDeferredResult(DeferredResult result) { - deferredResults.remove(result); - } - - public void rememberStreamBufferDuration(int streamId, int bufferDuration) { - streamBuffers.put(streamId - 1, bufferDuration); - } - - /** - * Set maximum time to wait for valid handshake in milliseconds. - * - * @param maxHandshakeTimeout Maximum time in milliseconds - */ - public void setMaxHandshakeTimeout(int maxHandshakeTimeout) { - this.maxHandshakeTimeout = maxHandshakeTimeout; - } - - public long getMaxHandlingTimeout() { - return maxHandlingTimeout; - } - - public void setMaxHandlingTimeout(long maxHandlingTimeout) { - this.maxHandlingTimeout = maxHandlingTimeout; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - if (log.isDebugEnabled()) { - String id = getClient() != null ? getClient().getId() : null; - return String.format("%1$s %2$s:%3$s to %4$s client: %5$s session: %6$s state: %7$s", new Object[] { getClass().getSimpleName(), getRemoteAddress(), getRemotePort(), getHost(), id, - getSessionId(), getState().states[getStateCode()] }); - } else { - Object[] args = new Object[] { getClass().getSimpleName(), getRemoteAddress(), getReadBytes(), getWrittenBytes(), getSessionId(), getState().states[getStateCode()] }; - return String.format("%1$s from %2$s (in: %3$s out: %4$s) session: %5$s state: %6$s", args); - } - } - - /** - * Specify the size of queue that will trigger audio packet dropping, disabled if it's 0 - * */ - public void setExecutorQueueSizeToDropAudioPackets( - Integer executorQueueSizeToDropAudioPackets) { - this.executorQueueSizeToDropAudioPackets = executorQueueSizeToDropAudioPackets; - } - - /** - * Task that keeps connection alive and disconnects if client is dead. - */ - private class KeepAliveTask implements Runnable { - - private final AtomicLong lastBytesRead = new AtomicLong(0); - - private volatile long lastBytesReadTime = 0; - - public void run() { - // we dont ping until in connected state - if (state.getState() == RTMP.STATE_CONNECTED) { - // ensure the job is not already running - if (running.compareAndSet(false, true)) { - if (log.isTraceEnabled()) - log.trace("Running keep-alive for {}", getSessionId()); - try { - // first check connected - if (isConnected()) { - // get now - long now = System.currentTimeMillis(); - // get the current bytes read count on the connection - long currentReadBytes = getReadBytes(); - // get our last bytes read count - long previousReadBytes = lastBytesRead.get(); - if (log.isTraceEnabled()) - log.trace("Time now: {} current read count: {} last read count: {}", new Object[] { now, currentReadBytes, previousReadBytes }); - if (currentReadBytes > previousReadBytes) { - if (log.isTraceEnabled()) - log.trace("Client is still alive, no ping needed"); - // client has sent data since last check and thus is not dead. No need to ping - if (lastBytesRead.compareAndSet(previousReadBytes, currentReadBytes)) { - // update the timestamp to match our update - lastBytesReadTime = now; - } - // check idle - if (isIdle()) { - onInactive(); - } - } else { - // client didn't send response to ping command and didn't sent data for too long, disconnect - long lastPingTime = lastPingSentOn.get(); - long lastPongTime = lastPongReceivedOn.get(); - if (lastPongTime > 0 && (lastPingTime - lastPongTime > maxInactivity) && (now - lastBytesReadTime > maxInactivity)) { - log.warn("Closing connection - inactivity timeout: session=[{}}, lastPongReceived=[{} ms ago], lastPingSent=[{} ms ago], lastDataRx=[{} ms ago]", new Object[] { getSessionId(), - (lastPingTime - lastPongTime), (now - lastPingTime), (now - lastBytesReadTime)}); - // the following line deals with a very common support request - log.warn("Client on session=[{}] has not responded to our ping for [{} ms] and we haven't received data for [{} ms]", - new Object[] { getSessionId(), (lastPingTime - lastPongTime), (now - lastBytesReadTime)}); - onInactive(); - } else { - // send ping command to client to trigger sending of data - ping(); - } - } - } else { - if (log.isDebugEnabled()) - log.debug("No longer connected, clean up connection. Connection state: {}", state.states[state.getState()]); - onInactive(); - } - } catch (Exception e) { - log.warn("Exception in keepalive for {}", getSessionId(), e); - } finally { - // reset running flag - running.compareAndSet(true, false); - } - } - } - } - } - - /** - * Task that waits for a valid handshake and disconnects the client if none is received. - */ - private class WaitForHandshakeTask implements Runnable { - - public void run() { - if (log.isTraceEnabled()) - log.trace("Running handshake-wait for {}", getSessionId()); - try { - Thread.sleep(maxHandshakeTimeout); - // check for connected state before disconnecting - if (state.getState() != RTMP.STATE_CONNECTED) { - // Client didn't send a valid handshake, disconnect - log.warn("Closing {}, due to long handshake. State: {}", getSessionId(), state.states[getStateCode()]); - onInactive(); - } - } catch (InterruptedException e) { - } - } - - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/RTMPHandler.java b/src/main/java/org/red5/server/net/rtmp/RTMPHandler.java deleted file mode 100644 index 27749c1a3..000000000 --- a/src/main/java/org/red5/server/net/rtmp/RTMPHandler.java +++ /dev/null @@ -1,622 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import java.util.HashMap; -import java.util.Map; - -import org.red5.io.object.StreamAction; -import org.red5.logging.Red5LoggerFactory; -import org.red5.server.api.IConnection.Encoding; -import org.red5.server.api.IContext; -import org.red5.server.api.IServer; -import org.red5.server.api.Red5; -import org.red5.server.api.scope.IBroadcastScope; -import org.red5.server.api.scope.IGlobalScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeHandler; -import org.red5.server.api.service.IPendingServiceCall; -import org.red5.server.api.service.IServiceCall; -import org.red5.server.api.so.ISharedObject; -import org.red5.server.api.so.ISharedObjectSecurity; -import org.red5.server.api.so.ISharedObjectSecurityService; -import org.red5.server.api.so.ISharedObjectService; -import org.red5.server.api.stream.IClientBroadcastStream; -import org.red5.server.api.stream.IClientStream; -import org.red5.server.api.stream.IStreamService; -import org.red5.server.exception.ClientRejectedException; -import org.red5.server.exception.ScopeNotFoundException; -import org.red5.server.exception.ScopeShuttingDownException; -import org.red5.server.messaging.IConsumer; -import org.red5.server.messaging.OOBControlMessage; -import org.red5.server.net.ICommand; -import org.red5.server.net.rtmp.codec.RTMP; -import org.red5.server.net.rtmp.event.ChunkSize; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.event.Ping; -import org.red5.server.net.rtmp.event.SetBuffer; -import org.red5.server.net.rtmp.event.StreamActionEvent; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.net.rtmp.status.Status; -import org.red5.server.net.rtmp.status.StatusObject; -import org.red5.server.net.rtmp.status.StatusObjectService; -import org.red5.server.service.Call; -import org.red5.server.so.ISharedObjectEvent; -import org.red5.server.so.SharedObjectEvent; -import org.red5.server.so.SharedObjectMessage; -import org.red5.server.so.SharedObjectService; -import org.red5.server.stream.StreamService; -import org.red5.server.util.ScopeUtils; -import org.slf4j.Logger; - -/** - * RTMP events handler. - */ -public class RTMPHandler extends BaseRTMPHandler { - - protected static Logger log = Red5LoggerFactory.getLogger(RTMPHandler.class); - - /** - * Status object service. - */ - protected StatusObjectService statusObjectService; - - /** - * Red5 server instance. - */ - protected IServer server; - - /** - * Whether or not global scope connections are allowed. - */ - private boolean globalScopeConnectionAllowed; - - /** - * Whether or not to dispatch stream actions to the current scope. - */ - private boolean dispatchStreamActions; - - /** - * Setter for server object. - * - * @param server Red5 server instance - */ - public void setServer(IServer server) { - this.server = server; - } - - /** - * Setter for status object service. - * - * @param statusObjectService Status object service. - */ - public void setStatusObjectService(StatusObjectService statusObjectService) { - this.statusObjectService = statusObjectService; - } - - public boolean isGlobalScopeConnectionAllowed() { - return globalScopeConnectionAllowed; - } - - public void setGlobalScopeConnectionAllowed(boolean globalScopeConnectionAllowed) { - this.globalScopeConnectionAllowed = globalScopeConnectionAllowed; - } - - /** - * @return the dispatchStreamActions - */ - public boolean isDispatchStreamActions() { - return dispatchStreamActions; - } - - /** - * @param dispatchStreamActions the dispatchStreamActions to set - */ - public void setDispatchStreamActions(boolean dispatchStreamActions) { - this.dispatchStreamActions = dispatchStreamActions; - } - - /** {@inheritDoc} */ - @Override - protected void onChunkSize(RTMPConnection conn, Channel channel, Header source, ChunkSize chunkSize) { - int requestedChunkSize = chunkSize.getSize(); - log.debug("Chunk size: {}", requestedChunkSize); - // set chunk size on the connection - RTMP state = conn.getState(); - // set only the read chunk size since it came from the client - state.setReadChunkSize(requestedChunkSize); - //state.setWriteChunkSize(requestedChunkSize); - // set on each of the streams - for (IClientStream stream : conn.getStreams()) { - if (stream instanceof IClientBroadcastStream) { - IClientBroadcastStream bs = (IClientBroadcastStream) stream; - IBroadcastScope scope = bs.getScope().getBroadcastScope(bs.getPublishedName()); - if (scope == null) { - continue; - } - OOBControlMessage setChunkSize = new OOBControlMessage(); - setChunkSize.setTarget("ClientBroadcastStream"); - setChunkSize.setServiceName("chunkSize"); - if (setChunkSize.getServiceParamMap() == null) { - setChunkSize.setServiceParamMap(new HashMap()); - } - setChunkSize.getServiceParamMap().put("chunkSize", requestedChunkSize); - scope.sendOOBControlMessage((IConsumer) null, setChunkSize); - log.debug("Sending chunksize {} to {}", chunkSize, bs.getProvider()); - } - } - } - - /** - * Remoting call invocation handler. - * - * @param conn RTMP connection - * @param call Service call - */ - protected void invokeCall(RTMPConnection conn, IServiceCall call) { - final IScope scope = conn.getScope(); - if (scope != null) { - if (scope.hasHandler()) { - final IScopeHandler handler = scope.getHandler(); - log.debug("Scope: {} handler: {}", scope, handler); - if (!handler.serviceCall(conn, call)) { - // XXX: What to do here? Return an error? - log.warn("Scope: {} handler failed on service call", scope.getName(), new Exception("Service call failed")); - return; - } - } - final IContext context = scope.getContext(); - log.debug("Context: {}", context); - context.getServiceInvoker().invoke(call, scope); - } else { - log.warn("Scope was null for invoke: {} connection state: {}", call.getServiceMethodName(), conn.getStateCode()); - } - } - - /** - * Remoting call invocation handler. - * - * @param conn - * RTMP connection - * @param call - * Service call - * @param service - * Server-side service object - * @return true if the call was performed, otherwise - * false - */ - private boolean invokeCall(RTMPConnection conn, IServiceCall call, Object service) { - final IScope scope = conn.getScope(); - final IContext context = scope.getContext(); - if (log.isTraceEnabled()) { - log.trace("Scope: {} context: {} service: {}", scope, context, service); - } - return context.getServiceInvoker().invoke(call, service); - } - - /** {@inheritDoc} */ - @SuppressWarnings({ "unchecked" }) - @Override - protected void onCommand(RTMPConnection conn, Channel channel, Header source, ICommand command) { - log.debug("onCommand {}", command); - // get the call - final IServiceCall call = command.getCall(); - log.trace("call: {}", call); - // get the method name - final String action = call.getServiceMethodName(); - // If it's a callback for server remote call then pass it over to callbacks handler and return - if ("_result".equals(action) || "_error".equals(action)) { - handlePendingCallResult(conn, (Invoke) command); - return; - } - boolean disconnectOnReturn = false; - // "connected" here means that there is a scope associated with the connection (post-"connect") - boolean connected = conn.isConnected(); - if (connected) { - // If this is not a service call then handle connection... - if (call.getServiceName() == null) { - StreamAction streamAction = StreamAction.getEnum(action); - if (log.isDebugEnabled()) { - log.debug("Stream action: {}", streamAction.toString()); - } - // TODO change this to an application scope parameter and / or change to the listener pattern - if (dispatchStreamActions) { - // pass the stream action event to the handler - try { - conn.getScope().getHandler().handleEvent(new StreamActionEvent(streamAction)); - } catch (Exception ex) { - log.warn("Exception passing stream action: {} to the scope handler", streamAction, ex); - } - } - //if the "stream" action is not predefined a custom type will be returned - switch (streamAction) { - case DISCONNECT: - conn.close(); - break; - case CREATE_STREAM: - case INIT_STREAM: - case CLOSE_STREAM: - case RELEASE_STREAM: - case DELETE_STREAM: - case PUBLISH: - case PLAY: - case PLAY2: - case SEEK: - case PAUSE: - case PAUSE_RAW: - case RECEIVE_VIDEO: - case RECEIVE_AUDIO: - IStreamService streamService = (IStreamService) ScopeUtils.getScopeService(conn.getScope(), IStreamService.class, StreamService.class); - Status status = null; - try { - log.debug("Invoking {} from {} with service: {}", new Object[] { call, conn, streamService }); - if (invokeCall(conn, call, streamService)) { - log.debug("Stream service invoke {} success", action); - } else { - status = getStatus(NS_INVALID_ARGUMENT).asStatus(); - status.setDescription(String.format("Failed to %s (stream id: %d)", action, source.getStreamId())); - } - } catch (Throwable err) { - log.error("Error while invoking {} on stream service. {}", action, err); - status = getStatus(NS_FAILED).asStatus(); - status.setDescription(String.format("Error while invoking %s (stream id: %d)", action, source.getStreamId())); - status.setDetails(err.getMessage()); - } - if (status != null) { - channel.sendStatus(status); - } else { - log.debug("Status for {} was null", action); - } - break; - default: - log.debug("Defaulting to invoke for: {}", action); - invokeCall(conn, call); - } - } else { - // handle service calls - invokeCall(conn, call); - } - } else { - if (StreamAction.CONNECT.equals(action)) { - // Handle connection - log.debug("connect"); - // Get parameters passed from client to - // NetConnection#connection - final Map params = command.getConnectionParams(); - // Get hostname - String host = getHostname((String) params.get("tcUrl")); - // app name as path, but without query string if there is one - String path = (String) params.get("app"); - if (path.indexOf("?") != -1) { - int idx = path.indexOf("?"); - params.put("queryString", path.substring(idx)); - path = path.substring(0, idx); - } - params.put("path", path); - // connection setup - conn.setup(host, path, params); - try { - // Lookup server scope when connected using host and application name - IGlobalScope global = server.lookupGlobal(host, path); - log.trace("Global lookup result: {}", global); - if (global != null) { - final IContext context = global.getContext(); - IScope scope = null; - try { - // TODO optimize this to use Scope instead of Context - scope = context.resolveScope(global, path); - // if global scope connection is not allowed, reject - if (scope.getDepth() < 1 && !globalScopeConnectionAllowed) { - call.setStatus(Call.STATUS_ACCESS_DENIED); - if (call instanceof IPendingServiceCall) { - IPendingServiceCall pc = (IPendingServiceCall) call; - StatusObject status = getStatus(NC_CONNECT_REJECTED); - status.setDescription("Global scope connection disallowed on this server."); - pc.setResult(status); - } - disconnectOnReturn = true; - } - if (scope != null) { - if (log.isTraceEnabled()) { - log.trace("Connecting to: {}", scope); - } - if (log.isDebugEnabled()) { - log.debug("Connecting to: {}", scope.getName()); - log.debug("Conn {}, scope {}, call {}", new Object[] { conn, scope, call }); - log.debug("Call args {}", call.getArguments()); - } - boolean okayToConnect; - try { - if (call.getArguments() != null) { - okayToConnect = conn.connect(scope, call.getArguments()); - } else { - okayToConnect = conn.connect(scope); - } - if (okayToConnect) { - log.debug("Connected - {}", conn.getClient()); - call.setStatus(Call.STATUS_SUCCESS_RESULT); - if (call instanceof IPendingServiceCall) { - IPendingServiceCall pc = (IPendingServiceCall) call; - //send fmsver and capabilities - StatusObject result = getStatus(NC_CONNECT_SUCCESS); - result.setAdditional("fmsVer", Red5.getFMSVersion()); - result.setAdditional("capabilities", Red5.getCapabilities()); - result.setAdditional("mode", Integer.valueOf(1)); - result.setAdditional("data", Red5.getDataVersion()); - pc.setResult(result); - } - // Measure initial roundtrip time after connecting - conn.ping(new Ping(Ping.STREAM_BEGIN, 0, -1)); - disconnectOnReturn = false; - } else { - log.debug("Connect failed"); - call.setStatus(Call.STATUS_ACCESS_DENIED); - if (call instanceof IPendingServiceCall) { - IPendingServiceCall pc = (IPendingServiceCall) call; - pc.setResult(getStatus(NC_CONNECT_REJECTED)); - } - disconnectOnReturn = true; - } - } catch (ClientRejectedException rejected) { - log.debug("Connect rejected"); - call.setStatus(Call.STATUS_ACCESS_DENIED); - if (call instanceof IPendingServiceCall) { - IPendingServiceCall pc = (IPendingServiceCall) call; - StatusObject status = getStatus(NC_CONNECT_REJECTED); - Object reason = rejected.getReason(); - if (reason != null) { - status.setApplication(reason); - //should we set description? - status.setDescription(reason.toString()); - } - pc.setResult(status); - } - disconnectOnReturn = true; - } - } - } catch (ScopeNotFoundException err) { - log.warn("Scope not found", err); - call.setStatus(Call.STATUS_SERVICE_NOT_FOUND); - if (call instanceof IPendingServiceCall) { - StatusObject status = getStatus(NC_CONNECT_REJECTED); - status.setDescription(String.format("No scope '%s' on this server.", path)); - ((IPendingServiceCall) call).setResult(status); - } - log.info("Scope {} not found on {}", path, host); - disconnectOnReturn = true; - } catch (ScopeShuttingDownException err) { - log.warn("Scope shutting down", err); - call.setStatus(Call.STATUS_APP_SHUTTING_DOWN); - if (call instanceof IPendingServiceCall) { - StatusObject status = getStatus(NC_CONNECT_APPSHUTDOWN); - status.setDescription(String.format("Application at '%s' is currently shutting down.", path)); - ((IPendingServiceCall) call).setResult(status); - } - log.info("Application at {} currently shutting down on {}", path, host); - disconnectOnReturn = true; - } - } else { - log.warn("Scope {} not found", path); - call.setStatus(Call.STATUS_SERVICE_NOT_FOUND); - if (call instanceof IPendingServiceCall) { - StatusObject status = getStatus(NC_CONNECT_INVALID_APPLICATION); - status.setDescription(String.format("No scope '%s' on this server.", path)); - ((IPendingServiceCall) call).setResult(status); - } - log.info("No application scope found for {} on host {}", path, host); - disconnectOnReturn = true; - } - } catch (RuntimeException e) { - call.setStatus(Call.STATUS_GENERAL_EXCEPTION); - if (call instanceof IPendingServiceCall) { - IPendingServiceCall pc = (IPendingServiceCall) call; - pc.setResult(getStatus(NC_CONNECT_FAILED)); - } - log.error("Error connecting {}", e); - disconnectOnReturn = true; - } - // Evaluate request for AMF3 encoding - if (Integer.valueOf(3).equals(params.get("objectEncoding"))) { - if (call instanceof IPendingServiceCall) { - Object pcResult = ((IPendingServiceCall) call).getResult(); - Map result; - if (pcResult instanceof Map) { - result = (Map) pcResult; - result.put("objectEncoding", 3); - } else if (pcResult instanceof StatusObject) { - result = new HashMap(); - StatusObject status = (StatusObject) pcResult; - result.put("code", status.getCode()); - result.put("description", status.getDescription()); - result.put("application", status.getApplication()); - result.put("level", status.getLevel()); - result.put("objectEncoding", 3); - ((IPendingServiceCall) call).setResult(result); - } - } - conn.getState().setEncoding(Encoding.AMF3); - } - } else { - // not connected and attempting to send an invoke - log.warn("Not connected, closing connection"); - conn.close(); - } - } - if (command instanceof Invoke) { - if ((source.getStreamId() != 0) && (call.getStatus() == Call.STATUS_SUCCESS_VOID || call.getStatus() == Call.STATUS_SUCCESS_NULL)) { - // This fixes a bug in the FP on Intel Macs. - log.debug("Method does not have return value, do not reply"); - return; - } - boolean sendResult = true; - if (call instanceof IPendingServiceCall) { - IPendingServiceCall psc = (IPendingServiceCall) call; - Object result = psc.getResult(); - if (result instanceof DeferredResult) { - // Remember the deferred result to be sent later - DeferredResult dr = (DeferredResult) result; - dr.setServiceCall(psc); - dr.setChannel(channel); - dr.setTransactionId(command.getTransactionId()); - conn.registerDeferredResult(dr); - sendResult = false; - } - } - if (sendResult) { - // The client expects a result for the method call. - Invoke reply = new Invoke(); - reply.setCall(call); - reply.setTransactionId(command.getTransactionId()); - channel.write(reply); - if (disconnectOnReturn) { - log.debug("Close connection due to connect handling exception: {}", conn.getSessionId()); - conn.close(); - } - } - } - } - - public StatusObject getStatus(String code) { - return statusObjectService.getStatusObject(code); - } - - /** {@inheritDoc} */ - @Override - protected void onPing(RTMPConnection conn, Channel channel, Header source, Ping ping) { - switch (ping.getEventType()) { - case Ping.CLIENT_BUFFER: - SetBuffer setBuffer = (SetBuffer) ping; - // get the stream id - int streamId = setBuffer.getStreamId(); - // get requested buffer size in milliseconds - int buffer = setBuffer.getBufferLength(); - log.debug("Client sent a buffer size: {} ms for stream id: {}", buffer, streamId); - IClientStream stream = null; - if (streamId != 0) { - // The client wants to set the buffer time - stream = conn.getStreamById(streamId); - if (stream != null) { - stream.setClientBufferDuration(buffer); - log.trace("Stream type: {}", stream.getClass().getName()); - } - } - //catch-all to make sure buffer size is set - if (stream == null) { - // Remember buffer time until stream is created - conn.rememberStreamBufferDuration(streamId, buffer); - log.debug("Remembering client buffer on stream: {}", buffer); - } - break; - case Ping.PONG_SERVER: - // This is the response to an IConnection.ping request - conn.pingReceived(ping); - break; - default: - log.warn("Unhandled ping: {}", ping); - } - } - - /** - * Create and send SO message stating that a SO could not be created. - * - * @param conn - * @param message Shared object message that incurred the failure - */ - private void sendSOCreationFailed(RTMPConnection conn, SharedObjectMessage message) { - log.debug("sendSOCreationFailed - message: {} conn: {}", message, conn); - // reset the object so we can re-use it - message.reset(); - // add the error event - message.addEvent(new SharedObjectEvent(ISharedObjectEvent.Type.CLIENT_STATUS, "error", SO_CREATION_FAILED)); - if (conn.isChannelUsed(3)) { - // XXX Paul: I dont like this direct write stuff, need to move to event-based - conn.getChannel(3).write(message); - } else { - log.warn("Channel is not in-use and cannot handle SO event: {}", message, new Exception("SO event handling failure")); - // XXX Paul: I dont like this direct write stuff, need to move to event-based - conn.getChannel(3).write(message); - } - } - - /** {@inheritDoc} */ - @Override - protected void onSharedObject(RTMPConnection conn, Channel channel, Header source, SharedObjectMessage message) { - if (log.isDebugEnabled()) { - log.debug("onSharedObject - conn: {} channel: {} so message: {}", new Object[] { conn.getSessionId(), channel.getId(), message }); - } - final IScope scope = conn.getScope(); - if (scope != null) { - // so name - String name = message.getName(); - // whether or not the incoming so is persistent - boolean persistent = message.isPersistent(); - // shared object service - ISharedObjectService sharedObjectService = (ISharedObjectService) ScopeUtils.getScopeService(scope, ISharedObjectService.class, SharedObjectService.class, false); - if (!sharedObjectService.hasSharedObject(scope, name)) { - log.debug("Shared object service doesnt have requested object, creation will be attempted"); - ISharedObjectSecurityService security = (ISharedObjectSecurityService) ScopeUtils.getScopeService(scope, ISharedObjectSecurityService.class); - if (security != null) { - // Check handlers to see if creation is allowed - for (ISharedObjectSecurity handler : security.getSharedObjectSecurity()) { - if (!handler.isCreationAllowed(scope, name, persistent)) { - log.debug("Shared object create failed, creation is not allowed"); - sendSOCreationFailed(conn, message); - return; - } - } - } - if (!sharedObjectService.createSharedObject(scope, name, persistent)) { - log.debug("Shared object create failed"); - sendSOCreationFailed(conn, message); - return; - } - } - ISharedObject so = sharedObjectService.getSharedObject(scope, name); - if (so != null) { - if (so.isPersistent() == persistent) { - log.debug("Dispatch persistent shared object"); - so.dispatchEvent(message); - } else { - log.warn("Shared object persistence mismatch - current: {} incoming: {}", so.isPersistent(), persistent); - // reset the object so we can re-use it - message.reset(); - // add the error event - message.addEvent(new SharedObjectEvent(ISharedObjectEvent.Type.CLIENT_STATUS, "error", SO_PERSISTENCE_MISMATCH)); - conn.getChannel(3).write(message); - } - } else { - log.warn("Shared object lookup returned null for {} in {}", name, scope.getName()); - // reset the object so we can re-use it - message.reset(); - // add the error event - message.addEvent(new SharedObjectEvent(ISharedObjectEvent.Type.CLIENT_STATUS, "error", NC_CALL_FAILED)); - conn.getChannel(3).write(message); - } - } else { - // The scope already has been deleted - log.debug("Shared object scope was not found"); - sendSOCreationFailed(conn, message); - } - } - - protected void onBWDone() { - log.debug("onBWDone"); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/RTMPHandshake.java b/src/main/java/org/red5/server/net/rtmp/RTMPHandshake.java deleted file mode 100644 index 0fb4a3f46..000000000 --- a/src/main/java/org/red5/server/net/rtmp/RTMPHandshake.java +++ /dev/null @@ -1,440 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import java.math.BigInteger; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.Security; -import java.security.spec.KeySpec; -import java.util.Random; - -import javax.crypto.Cipher; -import javax.crypto.KeyAgreement; -import javax.crypto.Mac; -import javax.crypto.interfaces.DHPublicKey; -import javax.crypto.spec.DHParameterSpec; -import javax.crypto.spec.DHPublicKeySpec; -import javax.crypto.spec.SecretKeySpec; - -import org.apache.commons.codec.binary.Hex; -import org.apache.mina.core.buffer.IoBuffer; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.red5.server.net.IHandshake; -import org.red5.server.net.rtmp.message.Constants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Generates and validates the RTMP handshake response for Flash Players. - * Client versions equal to or greater than Flash 9,0,124,0 require a nonzero - * value as the fifth byte of the handshake request. - * - * @author Jacinto Shy II (jacinto.m.shy@ieee.org) - * @author Steven Zimmer (stevenlzimmer@gmail.com) - * @author Gavriloaie Eugen-Andrei - * @author Ari-Pekka Viitanen - * @author Paul Gregoire - * @author Tiago Jacobs - */ -public abstract class RTMPHandshake implements IHandshake { - - protected static Logger log = LoggerFactory.getLogger(RTMPHandshake.class); - - //for old style handshake - public static byte[] HANDSHAKE_PAD_BYTES; - - protected static final byte[] GENUINE_FMS_KEY = { - (byte) 0x47, (byte) 0x65, (byte) 0x6e, (byte) 0x75, (byte) 0x69, (byte) 0x6e, (byte) 0x65, (byte) 0x20, - (byte) 0x41, (byte) 0x64, (byte) 0x6f, (byte) 0x62, (byte) 0x65, (byte) 0x20, (byte) 0x46, (byte) 0x6c, - (byte) 0x61, (byte) 0x73, (byte) 0x68, (byte) 0x20, (byte) 0x4d, (byte) 0x65, (byte) 0x64, (byte) 0x69, - (byte) 0x61, (byte) 0x20, (byte) 0x53, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65, (byte) 0x72, - (byte) 0x20, (byte) 0x30, (byte) 0x30, (byte) 0x31, // Genuine Adobe Flash Media Server 001 - (byte) 0xf0, (byte) 0xee, (byte) 0xc2, (byte) 0x4a, (byte) 0x80, (byte) 0x68, (byte) 0xbe, (byte) 0xe8, - (byte) 0x2e, (byte) 0x00, (byte) 0xd0, (byte) 0xd1, (byte) 0x02, (byte) 0x9e, (byte) 0x7e, (byte) 0x57, - (byte) 0x6e, (byte) 0xec, (byte) 0x5d, (byte) 0x2d, (byte) 0x29, (byte) 0x80, (byte) 0x6f, (byte) 0xab, - (byte) 0x93, (byte) 0xb8, (byte) 0xe6, (byte) 0x36, (byte) 0xcf, (byte) 0xeb, (byte) 0x31, (byte) 0xae}; - - protected static final byte[] GENUINE_FP_KEY = { - (byte) 0x47, (byte) 0x65, (byte) 0x6E, (byte) 0x75, (byte) 0x69, (byte) 0x6E, (byte) 0x65, (byte) 0x20, - (byte) 0x41, (byte) 0x64, (byte) 0x6F, (byte) 0x62, (byte) 0x65, (byte) 0x20, (byte) 0x46, (byte) 0x6C, - (byte) 0x61, (byte) 0x73, (byte) 0x68, (byte) 0x20, (byte) 0x50, (byte) 0x6C, (byte) 0x61, (byte) 0x79, - (byte) 0x65, (byte) 0x72, (byte) 0x20, (byte) 0x30, (byte) 0x30, (byte) 0x31, // Genuine Adobe Flash Player 001 - (byte) 0xF0, (byte) 0xEE, (byte) 0xC2, (byte) 0x4A, (byte) 0x80, (byte) 0x68, (byte) 0xBE, (byte) 0xE8, - (byte) 0x2E, (byte) 0x00, (byte) 0xD0, (byte) 0xD1, (byte) 0x02, (byte) 0x9E, (byte) 0x7E, (byte) 0x57, - (byte) 0x6E, (byte) 0xEC, (byte) 0x5D, (byte) 0x2D, (byte) 0x29, (byte) 0x80, (byte) 0x6F, (byte) 0xAB, - (byte) 0x93, (byte) 0xB8, (byte) 0xE6, (byte) 0x36, (byte) 0xCF, (byte) 0xEB, (byte) 0x31, (byte) 0xAE}; - - /** Modulus bytes from flazr */ - protected static final byte[] DH_MODULUS_BYTES = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xc9, (byte) 0x0f, (byte) 0xda, (byte) 0xa2, (byte) 0x21, - (byte) 0x68, (byte) 0xc2, (byte) 0x34, (byte) 0xc4, (byte) 0xc6, (byte) 0x62, (byte) 0x8b, (byte) 0x80, - (byte) 0xdc, (byte) 0x1c, (byte) 0xd1, (byte) 0x29, (byte) 0x02, (byte) 0x4e, (byte) 0x08, (byte) 0x8a, - (byte) 0x67, (byte) 0xcc, (byte) 0x74, (byte) 0x02, (byte) 0x0b, (byte) 0xbe, (byte) 0xa6, (byte) 0x3b, - (byte) 0x13, (byte) 0x9b, (byte) 0x22, (byte) 0x51, (byte) 0x4a, (byte) 0x08, (byte) 0x79, (byte) 0x8e, - (byte) 0x34, (byte) 0x04, (byte) 0xdd, (byte) 0xef, (byte) 0x95, (byte) 0x19, (byte) 0xb3, (byte) 0xcd, - (byte) 0x3a, (byte) 0x43, (byte) 0x1b, (byte) 0x30, (byte) 0x2b, (byte) 0x0a, (byte) 0x6d, (byte) 0xf2, - (byte) 0x5f, (byte) 0x14, (byte) 0x37, (byte) 0x4f, (byte) 0xe1, (byte) 0x35, (byte) 0x6d, (byte) 0x6d, - (byte) 0x51, (byte) 0xc2, (byte) 0x45, (byte) 0xe4, (byte) 0x85, (byte) 0xb5, (byte) 0x76, (byte) 0x62, - (byte) 0x5e, (byte) 0x7e, (byte) 0xc6, (byte) 0xf4, (byte) 0x4c, (byte) 0x42, (byte) 0xe9, (byte) 0xa6, - (byte) 0x37, (byte) 0xed, (byte) 0x6b, (byte) 0x0b, (byte) 0xff, (byte) 0x5c, (byte) 0xb6, (byte) 0xf4, - (byte) 0x06, (byte) 0xb7, (byte) 0xed, (byte) 0xee, (byte) 0x38, (byte) 0x6b, (byte) 0xfb, (byte) 0x5a, - (byte) 0x89, (byte) 0x9f, (byte) 0xa5, (byte) 0xae, (byte) 0x9f, (byte) 0x24, (byte) 0x11, (byte) 0x7c, - (byte) 0x4b, (byte) 0x1f, (byte) 0xe6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, (byte) 0xec, - (byte) 0xe6, (byte) 0x53, (byte) 0x81, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff }; - - protected static final BigInteger DH_MODULUS = new BigInteger(1, DH_MODULUS_BYTES); - - protected static final BigInteger DH_BASE = BigInteger.valueOf(2); - - protected static final int HANDSHAKE_SIZE_SERVER = (Constants.HANDSHAKE_SIZE * 2) + 1; - - protected static final int DIGEST_LENGTH = 32; - - protected static final int KEY_LENGTH = 128; - - protected static final Random random = new Random(); - - protected KeyAgreement keyAgreement; - - protected Cipher cipherOut; - - protected Cipher cipherIn; - - protected byte handshakeType; - - protected byte[] handshakeBytes; - - // validation scheme - protected int validationScheme = 0; - - // servers public key - protected byte[] incomingPublicKey; - - // clients public key - protected byte[] outgoingPublicKey; - - // swf verification bytes - protected byte[] swfVerificationBytes; - - private Mac hmacSHA256; - - static { - //get security provider - Security.addProvider(new BouncyCastleProvider()); - } - - public RTMPHandshake() { - log.trace("Handshake ctor"); - try { - hmacSHA256 = Mac.getInstance("HmacSHA256"); - } catch (SecurityException e) { - log.error("Security exception when getting HMAC", e); - } catch (NoSuchAlgorithmException e) { - log.error("HMAC SHA256 does not exist"); - } - //create our handshake bytes - createHandshakeBytes(); - } - - /** - * Calculates an HMAC SHA256 hash using a default key length. - * - * @param input - * @param key - * @return hmac hashed bytes - */ - public byte[] calculateHMAC_SHA256(byte[] input, byte[] key) { - byte[] output = null; - try { - hmacSHA256.init(new SecretKeySpec(key, "HmacSHA256")); - output = hmacSHA256.doFinal(input); - } catch (InvalidKeyException e) { - log.error("Invalid key", e); - } - return output; - } - - /** - * Calculates an HMAC SHA256 hash using a set key length. - * - * @param input - * @param key - * @param length - * @return hmac hashed bytes - */ - public byte[] calculateHMAC_SHA256(byte[] input, byte[] key, int length) { - byte[] output = null; - try { - hmacSHA256.init(new SecretKeySpec(key, 0, length, "HmacSHA256")); - output = hmacSHA256.doFinal(input); - } catch (InvalidKeyException e) { - log.error("Invalid key", e); - } - return output; - } - - /** - * Creates a Diffie-Hellman key pair. - * - * @return dh keypair - */ - protected KeyPair generateKeyPair() { - KeyPair keyPair = null; - DHParameterSpec keySpec = new DHParameterSpec(DH_MODULUS, DH_BASE); - try { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH"); - keyGen.initialize(keySpec); - keyPair = keyGen.generateKeyPair(); - keyAgreement = KeyAgreement.getInstance("DH"); - keyAgreement.init(keyPair.getPrivate()); - } catch (Exception e) { - log.error("Error generating keypair", e); - } - return keyPair; - } - - /** - * Returns the public key for a given key pair. - * - * @param keyPair - * @return public key - */ - protected static byte[] getPublicKey(KeyPair keyPair) { - DHPublicKey incomingPublicKey = (DHPublicKey) keyPair.getPublic(); - BigInteger dhY = incomingPublicKey.getY(); - log.debug("Public key: {}", dhY); - byte[] result = dhY.toByteArray(); - log.debug("Public key as bytes - length [{}]: {}", result.length, Hex.encodeHexString(result)); - byte[] temp = new byte[KEY_LENGTH]; - if (result.length < KEY_LENGTH) { - System.arraycopy(result, 0, temp, KEY_LENGTH - result.length, result.length); - result = temp; - log.debug("Padded public key length to 128"); - } else if (result.length > KEY_LENGTH){ - System.arraycopy(result, result.length - KEY_LENGTH, temp, 0, KEY_LENGTH); - result = temp; - log.debug("Truncated public key length to 128"); - } - return result; - } - - /** - * Determines the validation scheme for given input. - * - * @param otherPublicKeyBytes - * @param agreement - * @return shared secret bytes if client used a supported validation scheme - */ - protected static byte[] getSharedSecret(byte[] otherPublicKeyBytes, KeyAgreement agreement) { - BigInteger otherPublicKeyInt = new BigInteger(1, otherPublicKeyBytes); - try { - KeyFactory keyFactory = KeyFactory.getInstance("DH"); - KeySpec otherPublicKeySpec = new DHPublicKeySpec(otherPublicKeyInt, RTMPHandshake.DH_MODULUS, RTMPHandshake.DH_BASE); - PublicKey otherPublicKey = keyFactory.generatePublic(otherPublicKeySpec); - agreement.doPhase(otherPublicKey, true); - } catch (Exception e) { - log.error("Exception getting the shared secret", e); - } - byte[] sharedSecret = agreement.generateSecret(); - log.debug("Shared secret [{}]: {}", sharedSecret.length, Hex.encodeHexString(sharedSecret)); - return sharedSecret; - } - - /** - * Create the initial bytes for a request / response. - */ - protected abstract void createHandshakeBytes(); - - /** - * Determines the validation scheme for given input. - * - * @param input - * @return true if its a supported validation scheme, false if unsupported - */ - public abstract boolean validate(IoBuffer input); - - /** - * Returns the DH offset from an array of bytes. - * - * @param bytes - * @return DH offset - */ - protected int getDHOffset(byte[] bytes) { - int dhOffset = -1; - switch (validationScheme) { - case 1: - dhOffset = getDHOffset1(bytes); - break; - default: - log.debug("Scheme 0 will be used for DH offset"); - case 0: - dhOffset = getDHOffset0(bytes); - } - return dhOffset; - } - - /** - * Returns the DH byte offset. - * - * @return dh offset - */ - protected int getDHOffset0(byte[] bytes) { - int offset = (bytes[1532] & 0x0ff) + (bytes[1533] & 0x0ff) + (bytes[1534] & 0x0ff) + (bytes[1535] & 0x0ff); - offset = offset % 632; - offset = offset + 772; - if (offset + KEY_LENGTH >= 1536) { - log.error("Invalid DH offset"); - } - return offset; - } - - /** - * Returns the DH byte offset. - * - * @return dh offset - */ - protected int getDHOffset1(byte[] bytes) { - int offset = (bytes[768] & 0x0ff) + (bytes[769] & 0x0ff) + (bytes[770] & 0x0ff) + (bytes[771] & 0x0ff); - offset = offset % 632; - offset = offset + 8; - if (offset + KEY_LENGTH >= 1536) { - log.error("Invalid DH offset"); - } - return offset; - } - - /** - * Returns the digest offset using current validation scheme. - * - * @param pBuffer - * @return digest offset - */ - protected int getDigestOffset(byte[] pBuffer) { - int serverDigestOffset = -1; - switch (validationScheme) { - case 1: - serverDigestOffset = getDigestOffset1(pBuffer); - break; - default: - log.debug("Scheme 0 will be used for DH offset"); - case 0: - serverDigestOffset = getDigestOffset0(pBuffer); - } - return serverDigestOffset; - } - - /** - * Returns a digest byte offset. - * - * @param pBuffer source for digest data - * @return digest offset - */ - protected int getDigestOffset0(byte[] pBuffer) { - if (log.isTraceEnabled()) { - log.trace("Scheme 0 offset bytes {},{},{},{}", new Object[]{(pBuffer[8] & 0x0ff), (pBuffer[9] & 0x0ff), (pBuffer[10] & 0x0ff), (pBuffer[11] & 0x0ff)}); - } - int offset = (pBuffer[8] & 0x0ff) + (pBuffer[9] & 0x0ff) + (pBuffer[10] & 0x0ff) + (pBuffer[11] & 0x0ff); - offset = offset % 728; - offset = offset + 12; - if (offset + DIGEST_LENGTH >= 1536) { - log.error("Invalid digest offset"); - } - return offset; - } - - /** - * Returns a digest byte offset. - * - * @param pBuffer source for digest data - * @return digest offset - */ - protected int getDigestOffset1(byte[] pBuffer) { - if (log.isTraceEnabled()) { - log.trace("Scheme 1 offset bytes {},{},{},{}", new Object[]{(pBuffer[772] & 0x0ff), (pBuffer[773] & 0x0ff), (pBuffer[774] & 0x0ff), (pBuffer[775] & 0x0ff)}); - } - int offset = (pBuffer[772] & 0x0ff) + (pBuffer[773] & 0x0ff) + (pBuffer[774] & 0x0ff) + (pBuffer[775] & 0x0ff); - offset = offset % 728; - offset = offset + 776; - if (offset + DIGEST_LENGTH >= 1536) { - log.error("Invalid digest offset"); - } - return offset; - } - - /** - * Creates the servers handshake bytes - */ - public byte[] getHandshakeBytes() { - return handshakeBytes; - } - - /** - * Sets the handshake type. Currently only two types are supported, plain and encrypted. - * - * @param handshakeType - */ - public void setHandshakeType(byte handshakeType) { - log.trace("Setting handshake type: {}", handshakeType); - this.handshakeType = handshakeType; - } - - /** - * Returns the handshake type. - * - * @return handshakeType - */ - public byte getHandshakeType() { - return handshakeType; - } - - /** - * Gets the DH offset in the handshake bytes array based on validation scheme - * Generates DH keypair - * Adds public key to handshake bytes - */ - public Cipher getCipherOut() { - return cipherOut; - } - - /** - * Returns the contained handshake bytes. These are just random bytes - * if the player is using an non-versioned player. - * - * @return handshake bytes - */ - public Cipher getCipherIn() { - return cipherIn; - } - - /** - * Returns the SWF verification bytes. - * - * @return swf verification bytes - */ - public byte[] getSwfVerificationBytes() { - return swfVerificationBytes; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/RTMPMinaConnection.java b/src/main/java/org/red5/server/net/rtmp/RTMPMinaConnection.java deleted file mode 100644 index 0bd034901..000000000 --- a/src/main/java/org/red5/server/net/rtmp/RTMPMinaConnection.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import java.beans.ConstructorProperties; -import java.lang.management.ManagementFactory; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.Collections; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.management.StandardMBean; - -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.future.CloseFuture; -import org.apache.mina.core.future.IoFutureListener; -import org.apache.mina.core.session.IoSession; -import org.red5.server.api.scope.IScope; -import org.red5.server.jmx.mxbeans.RTMPMinaConnectionMXBean; -import org.red5.server.net.rtmp.codec.RTMP; -import org.red5.server.net.rtmp.event.ClientBW; -import org.red5.server.net.rtmp.event.ServerBW; -import org.red5.server.net.rtmp.message.Packet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; - -/** - * Represents an RTMP connection using Mina. - * - * @see "http://mina.apache.org/report/trunk/apidocs/org/apache/mina/core/session/IoSession.html" - * - * @author Paul Gregoire - */ -@ManagedResource -public class RTMPMinaConnection extends RTMPConnection implements RTMPMinaConnectionMXBean { - - protected static Logger log = LoggerFactory.getLogger(RTMPMinaConnection.class); - - /** - * MINA I/O session, connection between two end points - */ - private IoSession ioSession; - - /** - * MBean object name used for de/registration purposes. - */ - private ObjectName oName; - - protected int defaultServerBandwidth = 10000000; - - protected int defaultClientBandwidth = 10000000; - - protected boolean bandwidthDetection = true; - - /** Constructs a new RTMPMinaConnection. */ - @ConstructorProperties(value = { "persistent" }) - public RTMPMinaConnection() { - super(PERSISTENT); - } - - @SuppressWarnings("cast") - @Override - public boolean connect(IScope newScope, Object[] params) { - log.debug("Connect scope: {}", newScope); - boolean success = super.connect(newScope, params); - if (success) { - // tell the flash player how fast we want data and how fast we shall - // send it - getChannel(2).write(new ServerBW(defaultServerBandwidth)); - // second param is the limit type (0=hard,1=soft,2=dynamic) - getChannel(2).write(new ClientBW(defaultClientBandwidth, (byte) limitType)); - // if the client is null for some reason, skip the jmx registration - if (client != null) { - // perform bandwidth detection - if (bandwidthDetection && !client.isBandwidthChecked()) { - client.checkBandwidth(); - } - } else { - log.warn("Client was null"); - } - registerJMX(); - } else { - log.debug("Connect failed"); - } - return success; - } - - /** {@inheritDoc} */ - @Override - public void close() { - super.close(); - log.debug("IO Session closing: {}", (ioSession != null ? ioSession.isClosing() : null)); - if (ioSession != null && !ioSession.isClosing()) { - // accept no further incoming data - // ioSession.suspendRead(); - // close now, no flushing, no waiting - final CloseFuture future = ioSession.close(true); - log.debug("Connection close future: {}", future); - IoFutureListener listener = new IoFutureListener() { - public void operationComplete(CloseFuture future) { - if (future.isClosed()) { - log.debug("Connection is closed"); - log.trace("Session id - local: {} session: {}", getSessionId(), (String) ioSession.removeAttribute(RTMPConnection.RTMP_SESSION_ID)); - RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); - if (conn != null) { - handler.connectionClosed(conn); - } - } else { - log.debug("Connection is not yet closed"); - } - future.removeListener(this); - } - }; - future.addListener(listener); - } - log.debug("Connection state: {}", getState()); - if (getStateCode() != RTMP.STATE_DISCONNECTED) { - handler.connectionClosed(this); - } - // de-register with JMX - unregisterJMX(); - } - - /** - * Return MINA I/O session. - * - * @return MINA O/I session, connection between two end-points - */ - public IoSession getIoSession() { - return ioSession; - } - - /** - * @return the defaultServerBandwidth - */ - public int getDefaultServerBandwidth() { - return defaultServerBandwidth; - } - - /** - * @param defaultServerBandwidth - * the defaultServerBandwidth to set - */ - public void setDefaultServerBandwidth(int defaultServerBandwidth) { - this.defaultServerBandwidth = defaultServerBandwidth; - } - - /** - * @return the defaultClientBandwidth - */ - public int getDefaultClientBandwidth() { - return defaultClientBandwidth; - } - - /** - * @param defaultClientBandwidth - * the defaultClientBandwidth to set - */ - public void setDefaultClientBandwidth(int defaultClientBandwidth) { - this.defaultClientBandwidth = defaultClientBandwidth; - } - - /** - * @return the limitType - */ - public int getLimitType() { - return limitType; - } - - /** - * @param limitType - * the limitType to set - */ - public void setLimitType(int limitType) { - this.limitType = limitType; - } - - @Override - public void setExecutor(ThreadPoolTaskExecutor executor) { - this.executor = executor; - } - - /** - * @return the bandwidthDetection - */ - public boolean isBandwidthDetection() { - return bandwidthDetection; - } - - /** - * @param bandwidthDetection - * the bandwidthDetection to set - */ - public void setBandwidthDetection(boolean bandwidthDetection) { - this.bandwidthDetection = bandwidthDetection; - } - - /** {@inheritDoc} */ - @Override - public boolean isReaderIdle() { - if (ioSession != null) { - return ioSession.isReaderIdle(); - } - return true; - } - - /** {@inheritDoc} */ - @Override - public boolean isWriterIdle() { - if (ioSession != null) { - return ioSession.isWriterIdle(); - } - return true; - } - - /** {@inheritDoc} */ - @Override - public long getPendingMessages() { - if (ioSession != null) { - return ioSession.getScheduledWriteMessages(); - } - return 0; - } - - /** {@inheritDoc} */ - @Override - public long getReadBytes() { - if (ioSession != null) { - return ioSession.getReadBytes(); - } - return 0; - } - - /** {@inheritDoc} */ - @Override - public long getWrittenBytes() { - if (ioSession != null) { - return ioSession.getWrittenBytes(); - } - return 0; - } - - public void invokeMethod(String method) { - invoke(method); - } - - /** {@inheritDoc} */ - @Override - public boolean isConnected() { - log.debug("Connected: {}", (ioSession != null && ioSession.isConnected())); - // XXX Paul: not sure isClosing is actually working as we expect here - return super.isConnected() && (ioSession != null && ioSession.isConnected()); - } - - /** {@inheritDoc} */ - @Override - public boolean isIdle() { - if (ioSession != null) { - log.debug("Connection idle - read: {} write: {}", ioSession.isReaderIdle(), ioSession.isWriterIdle()); - return super.isIdle() && ioSession.isBothIdle(); - } - return super.isIdle(); - } - - /** {@inheritDoc} */ - @Override - protected void onInactive() { - close(); - } - - /** - * Setter for MINA I/O session (connection). - * - * @param protocolSession - * Protocol session - */ - public void setIoSession(IoSession protocolSession) { - SocketAddress remote = protocolSession.getRemoteAddress(); - if (remote instanceof InetSocketAddress) { - remoteAddress = ((InetSocketAddress) remote).getAddress().getHostAddress(); - remotePort = ((InetSocketAddress) remote).getPort(); - } else { - remoteAddress = remote.toString(); - remotePort = -1; - } - remoteAddresses = new ArrayList(1); - remoteAddresses.add(remoteAddress); - remoteAddresses = Collections.unmodifiableList(remoteAddresses); - this.ioSession = protocolSession; - log.trace("setIoSession conn: {}", this); - } - - /** {@inheritDoc} */ - @Override - public void write(Packet out) { - if (ioSession != null) { - final Semaphore lock = getLock(); - log.trace("Write lock wait count: {} closed: {}", lock.getQueueLength(), isClosed()); - while (!isClosed()) { - boolean acquired = false; - try { - acquired = lock.tryAcquire(10, TimeUnit.MILLISECONDS); - if (acquired) { - log.trace("Writing message"); - writingMessage(out); - ioSession.write(out); - break; - } - } catch (InterruptedException e) { - log.warn("Interrupted while waiting for write lock. State: {}", state.states[state.getState()], e); - String exMsg = e.getMessage(); - // if the exception cause is null break out of here to - // prevent looping until closed - if (exMsg == null || exMsg.indexOf("null") >= 0) { - log.debug("Exception writing to connection: {}", this); - break; - } - } finally { - if (acquired) { - lock.release(); - } - } - } - } - } - - /** {@inheritDoc} */ - @Override - public void writeRaw(IoBuffer out) { - if (ioSession != null) { - final Semaphore lock = getLock(); - while (!isClosed()) { - boolean acquired = false; - try { - acquired = lock.tryAcquire(10, TimeUnit.MILLISECONDS); - if (acquired) { - log.trace("Writing raw message"); - ioSession.write(out); - break; - } - } catch (InterruptedException e) { - log.warn("Interrupted while waiting for write lock (writeRaw). State: {}", state.states[state.getState()], e); - String exMsg = e.getMessage(); - // if the exception cause is null break out of here to - // prevent looping until closed - if (exMsg == null || exMsg.indexOf("null") >= 0) { - log.debug("Exception writing to connection: {}", this); - break; - } - } finally { - if (acquired) { - lock.release(); - } - } - } - } - } - - protected void registerJMX() { - // register with jmx - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - try { - String cName = this.getClass().getName(); - if (cName.indexOf('.') != -1) { - cName = cName.substring(cName.lastIndexOf('.')).replaceFirst("[\\.]", ""); - } - String hostStr = host; - int port = 1935; - if (host != null && host.indexOf(":") > -1) { - String[] arr = host.split(":"); - hostStr = arr[0]; - port = Integer.parseInt(arr[1]); - } - // Create a new mbean for this instance - oName = new ObjectName(String.format("org.red5.server:type=%s,connectionType=%s,host=%s,port=%d,clientId=%s", cName, type, hostStr, port, client.getId())); - if (!mbs.isRegistered(oName)) { - mbs.registerMBean(new StandardMBean(this, RTMPMinaConnectionMXBean.class, true), oName); - } else { - log.debug("Connection is already registered in JMX"); - } - } catch (Exception e) { - log.warn("Error on jmx registration", e); - } - } - - protected void unregisterJMX() { - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - if (oName != null && mbs.isRegistered(oName)) { - try { - mbs.unregisterMBean(oName); - } catch (Exception e) { - log.warn("Exception unregistering: {}", oName, e); - } - oName = null; - } - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/RTMPMinaIoHandler.java b/src/main/java/org/red5/server/net/rtmp/RTMPMinaIoHandler.java deleted file mode 100644 index adb8a462b..000000000 --- a/src/main/java/org/red5/server/net/rtmp/RTMPMinaIoHandler.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import java.io.IOException; - -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.future.CloseFuture; -import org.apache.mina.core.future.IoFutureListener; -import org.apache.mina.core.service.IoHandlerAdapter; -import org.apache.mina.core.service.IoProcessor; -import org.apache.mina.core.session.IoSession; -import org.apache.mina.core.write.WriteRequestQueue; -import org.apache.mina.filter.codec.ProtocolCodecFactory; -import org.apache.mina.filter.codec.ProtocolCodecFilter; -import org.red5.server.net.rtmp.codec.RTMP; -import org.red5.server.net.rtmp.message.Packet; -import org.red5.server.net.rtmpe.RTMPEIoFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Handles all RTMP protocol events fired by the MINA framework. - */ -public class RTMPMinaIoHandler extends IoHandlerAdapter { - - private static Logger log = LoggerFactory.getLogger(RTMPMinaIoHandler.class); - - /** - * RTMP events handler - */ - protected IRTMPHandler handler; - - protected ProtocolCodecFactory codecFactory; - - /** {@inheritDoc} */ - @Override - public void sessionCreated(IoSession session) throws Exception { - log.debug("Session created"); - // add rtmpe filter - session.getFilterChain().addFirst("rtmpeFilter", new RTMPEIoFilter()); - // add protocol filter next - session.getFilterChain().addLast("protocolFilter", new ProtocolCodecFilter(codecFactory)); - // create a connection - RTMPMinaConnection conn = createRTMPMinaConnection(); - // add session to the connection - conn.setIoSession(session); - // add the handler - conn.setHandler(handler); - // add the connections session id for look up using the connection manager - session.setAttribute(RTMPConnection.RTMP_SESSION_ID, conn.getSessionId()); - // add the in-bound handshake - session.setAttribute(RTMPConnection.RTMP_HANDSHAKE, new InboundHandshake()); - } - - /** {@inheritDoc} */ - @Override - public void sessionOpened(IoSession session) throws Exception { - String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - log.debug("Session opened: {} id: {}", session.getId(), sessionId); - RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); - handler.connectionOpened(conn); - } - - /** {@inheritDoc} */ - @Override - public void sessionClosed(IoSession session) throws Exception { - String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - log.debug("Session closed: {} id: {}", session.getId(), sessionId); - if (log.isTraceEnabled()) { - log.trace("Session attributes: {}", session.getAttributeKeys()); - } - if (sessionId != null) { - RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); - if (conn != null) { - // fire-off closed event - handler.connectionClosed(conn); - // clear any session attributes we may have previously set - // TODO: verify this cleanup code is necessary. The session is over and will be garbage collected surely? - if (session.containsAttribute(RTMPConnection.RTMP_HANDSHAKE)) { - session.removeAttribute(RTMPConnection.RTMP_HANDSHAKE); - } - if (session.containsAttribute(RTMPConnection.RTMPE_CIPHER_IN)) { - session.removeAttribute(RTMPConnection.RTMPE_CIPHER_IN); - session.removeAttribute(RTMPConnection.RTMPE_CIPHER_OUT); - } - } else { - log.warn("Connection was not found for {}", sessionId); - } - } else { - log.debug("Connections session id was null in session, may already be closed"); - } - } - - /** - * Handle raw buffer receiving event. - * - * @param in - * Data buffer - * @param session - * I/O session, that is, connection between two endpoints - */ - protected void rawBufferRecieved(IoBuffer in, IoSession session) { - if (log.isTraceEnabled()) { - log.trace("rawBufferRecieved: {}", in); - } - String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - log.trace("Session id: {}", sessionId); - RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); - RTMPHandshake handshake = (RTMPHandshake) session.getAttribute(RTMPConnection.RTMP_HANDSHAKE); - if (handshake != null) { - if (conn.getStateCode() != RTMP.STATE_HANDSHAKE) { - log.warn("Raw buffer after handshake, something odd going on"); - } - log.debug("Handshake - server phase 1 - size: {}", in.remaining()); - IoBuffer out = handshake.doHandshake(in); - if (out != null) { - log.trace("Output: {}", out); - session.write(out); - //if we are connected and doing encryption, add the ciphers - if (conn.getStateCode() == RTMP.STATE_CONNECTED) { - // remove handshake from session now that we are connected - // if we are using encryption then put the ciphers in the session - if (handshake.getHandshakeType() == RTMPConnection.RTMP_ENCRYPTED) { - log.debug("Adding ciphers to the session"); - session.setAttribute(RTMPConnection.RTMPE_CIPHER_IN, handshake.getCipherIn()); - session.setAttribute(RTMPConnection.RTMPE_CIPHER_OUT, handshake.getCipherOut()); - } - } - } - } else { - log.warn("Handshake was not found for this connection: {}", conn); - log.debug("Session: {}", session.getId()); - } - } - - /** {@inheritDoc} */ - @Override - public void messageReceived(IoSession session, Object message) throws Exception { - String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - log.trace("Message received on session: {} id: {}", session.getId(), sessionId); - if (message instanceof IoBuffer) { - rawBufferRecieved((IoBuffer) message, session); - } else { - log.trace("Session id: {}", sessionId); - RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); - if (conn != null) { - byte state = conn.getStateCode(); - // checking the state before allowing a task to be created will hopefully prevent rejected task exceptions - if (state != RTMP.STATE_DISCONNECTING && state != RTMP.STATE_DISCONNECTED) { - conn.handleMessageReceived((Packet) message); - } else { - log.info("Ignoring received message on {} due to state: {}", sessionId, conn.getState().states[state]); - } - } else { - log.warn("Connection was not found for {}", sessionId); - forceClose(session); - } - } - } - - /** {@inheritDoc} */ - @Override - public void messageSent(IoSession session, Object message) throws Exception { - String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - log.trace("Message sent on session: {} id: {}", session.getId(), sessionId); - RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); - if (conn != null) { - byte state = conn.getStateCode(); - if (state != RTMP.STATE_DISCONNECTING && state != RTMP.STATE_DISCONNECTED) { - if (message instanceof Packet) { - handler.messageSent(conn, (Packet) message); - } else { - log.debug("Message was not of Packet type; its type: {}", message != null ? message.getClass().getName() : "null"); - } - } - } else { - log.warn("Destination connection was null, it is already disposed. Session id: {}", sessionId); - } - } - - /** {@inheritDoc} */ - @Override - public void exceptionCaught(IoSession session, Throwable cause) throws Exception { - String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - if (log.isDebugEnabled()) { - log.debug("Exception caught on session: {} id: {}", session.getId(), sessionId, cause); - cause.printStackTrace(); - } - if (cause instanceof IOException) { - // Mina states that the connection will be automatically closed when an IOException is caught - log.debug("IOException caught on {}", sessionId); - } else { - log.debug("Non-IOException caught on {}", sessionId); - forceClose(session); - } - } - - /** - * Force the NioSession to be released and cleaned up. - * - * @param session - */ - private void forceClose(final IoSession session) { - log.warn("Force close - session: {}", session.getId()); - if (session.containsAttribute("FORCED_CLOSE")) { - log.info("Close already forced on this session: {}", session.getId()); - } else { - // set flag - session.setAttribute("FORCED_CLOSE", Boolean.TRUE); - // clean up - final String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - log.debug("Forcing close on session: {} id: {}", session.getId(), sessionId); - log.debug("Session closing: {}", session.isClosing()); - session.suspendRead(); - final WriteRequestQueue writeQueue = session.getWriteRequestQueue(); - if (writeQueue != null && !writeQueue.isEmpty(session)) { - log.debug("Clearing write queue"); - try { - writeQueue.clear(session); - } catch (Exception ex) { - // clear seems to cause a write to closed session ex in some cases - log.warn("Exception clearing write queue for {}", sessionId, ex); - } - } - // force close the session - final CloseFuture future = session.close(true); - IoFutureListener listener = new IoFutureListener() { - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void operationComplete(CloseFuture future) { - // now connection should be closed - log.debug("Close operation completed {}: {}", sessionId, future.isClosed()); - future.removeListener(this); - for (Object key : session.getAttributeKeys()) { - Object obj = session.getAttribute(key); - log.debug("Attribute: {}", obj.getClass().getName()); - if (obj instanceof IoProcessor) { - log.debug("Flushing session in processor"); - ((IoProcessor) obj).flush(session); - log.debug("Removing session from processor"); - ((IoProcessor) obj).remove(session); - } else if (obj instanceof IoBuffer) { - log.debug("Clearing session buffer"); - ((IoBuffer) obj).clear(); - ((IoBuffer) obj).free(); - } - } - } - }; - future.addListener(listener); - } - } - - /** - * Setter for handler. - * - * @param handler RTMP events handler - */ - public void setHandler(IRTMPHandler handler) { - this.handler = handler; - } - - /** - * @param codecFactory the codecFactory to set - */ - public void setCodecFactory(ProtocolCodecFactory codecFactory) { - this.codecFactory = codecFactory; - } - - protected RTMPMinaConnection createRTMPMinaConnection() { - return (RTMPMinaConnection) RTMPConnManager.getInstance().createConnection(RTMPMinaConnection.class); - } -} diff --git a/src/main/java/org/red5/server/net/rtmp/RTMPType.java b/src/main/java/org/red5/server/net/rtmp/RTMPType.java deleted file mode 100644 index bcde7e069..000000000 --- a/src/main/java/org/red5/server/net/rtmp/RTMPType.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -/** - * Enum for RTMP types. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public enum RTMPType { - - /** - * The specification refers to the following types by different names: - * 0x03 = Acknowledgement - * 0x04 = User control message - * 0x05 = Window Acknowledgement Size - * 0x06 = Set Peer Bandwidth - * 0x0f = AMF3 Data message - * 0x10 = AMF3 Shared object message - * 0x11 = AMF3 Command message - * 0x12 = AMF0 Data message - * 0x13 = AMF0 Shared object message - * 0x14 = AMF0 Command message - * - * ------------------------------------------------------------------- - * RTMFP related (here for reference) - * - * 0x30 Initiator hello - * 0x70 Responder hello - * 0x38 Initiator initial keying - * 0x78 Responder initial keying - * 0x0f Forwarded initiator hello - * 0x71 Forwarded hello response - * 0x10 Normal user data - * 0x11 Next user data - * 0x0c Session failed on client side - * 0x4c Session died - * 0x01 Causes response with 0x41, reset keep alive - * 0x41 Reset times keep alive - * 0x5e Negative ack - * 0x51 Some ack - */ - TYPE_CHUNK_SIZE(0x01), TYPE_ABORT(0x02), TYPE_BYTES_READ(0x03), TYPE_PING(0x04), TYPE_SERVER_BANDWIDTH(0x05), TYPE_CLIENT_BANDWIDTH(0x06), TYPE_EDGE_ORIGIN( - 0x07), TYPE_AUDIO_DATA(0x08), TYPE_VIDEO_DATA(0x09), TYPE_UNK1(0x0a), TYPE_UNK2(0x0b), TYPE_UNK3(0x0c), TYPE_UNK4(0x0d), TYPE_UNK5(0x0e), TYPE_FLEX_STREAM_SEND(0x0f), TYPE_FLEX_SHARED_OBJECT( - 0x10), TYPE_FLEX_MESSAGE(0x11), TYPE_NOTIFY(0x12), TYPE_SHARED_OBJECT(0x13), TYPE_INVOKE(0x14), TYPE_UNK6(0x15), TYPE_AGGREGATE(0x16), TYPE_UNK7(0x17), TYPE_UNK8(0x18); - - private final byte type; - - RTMPType(byte type) { - this.type = type; - } - - RTMPType(int type) { - this.type = (byte) type; - } - - public byte getType() { - return type; - } - - public static String valueOf(byte dataType) { - int idx = (int) dataType - 1; - if (idx < RTMPType.values().length) { - return RTMPType.values()[idx].name(); - } else { - return "TYPE_UNKNOWN:" + dataType; - } - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/RTMPUtils.java b/src/main/java/org/red5/server/net/rtmp/RTMPUtils.java deleted file mode 100644 index 47481462a..000000000 --- a/src/main/java/org/red5/server/net/rtmp/RTMPUtils.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.net.rtmp.message.Constants; - -/** - * RTMP utilities class. - * - * @author The Red5 Project - * @author Luke Hubbard, Codegent Ltd (luke@codegent.com) - * @author Art Clarke (aclarke@xuggle.com) - */ -public class RTMPUtils implements Constants { - /** - * Writes reversed integer to buffer. - * - * @param out Buffer - * @param value Integer to write - */ - public static void writeReverseIntOld(IoBuffer out, int value) { - byte[] bytes = new byte[4]; - IoBuffer rev = IoBuffer.allocate(4); - rev.putInt(value); - rev.flip(); - bytes[3] = rev.get(); - bytes[2] = rev.get(); - bytes[1] = rev.get(); - bytes[0] = rev.get(); - out.put(bytes); - rev.free(); - rev = null; - } - - /** - * Writes reversed integer to buffer. - * - * @param out Buffer - * @param value Integer to write - */ - public static void writeReverseInt(IoBuffer out, int value) { - out.put((byte) (0xFF & value)); - out.put((byte) (0xFF & (value >> 8))); - out.put((byte) (0xFF & (value >> 16))); - out.put((byte) (0xFF & (value >> 24))); - } - - /** - * - * @param out output buffer - * @param value value to write - */ - public static void writeMediumInt(IoBuffer out, int value) { - out.put((byte) (0xFF & (value >> 16))); - out.put((byte) (0xFF & (value >> 8))); - out.put((byte) (0xFF & (value >> 0))); - } - - /** - * - * @param in input - * @return unsigned int - */ - public static int readUnsignedMediumInt(IoBuffer in) { - final byte a = in.get(); - final byte b = in.get(); - final byte c = in.get(); - int val = 0; - val += (a & 0xff) << 16; - val += (b & 0xff) << 8; - val += (c & 0xff); - return val; - } - - /** - * - * @param in input - * @return unsigned medium (3 byte) int. - */ - public static int readUnsignedMediumIntOld(IoBuffer in) { - byte[] bytes = new byte[3]; - in.get(bytes); - int val = 0; - val += (bytes[0] & 0xFF) * 256 * 256; - val += (bytes[1] & 0xFF) * 256; - val += (bytes[2] & 0xFF); - return val; - } - - /** - * - * @param in input - * @return signed 3-byte int - */ - public static int readMediumIntOld(IoBuffer in) { - IoBuffer buf = IoBuffer.allocate(4); - buf.put((byte) 0x00); - buf.put(in.get()); - buf.put(in.get()); - buf.put(in.get()); - buf.flip(); - int value = buf.getInt(); - buf.free(); - buf = null; - return value; - } - - /** - * - * @param in input - * @return signed 3 byte int - */ - public static int readMediumInt(IoBuffer in) { - final byte a = in.get(); - final byte b = in.get(); - final byte c = in.get(); - // Fix unsigned values - int val = 0; - val += (a & 0xff) << 16; - val += (b & 0xff) << 8; - val += (c & 0xff); - return val; - } - - /** - * Read integer in reversed order. - * - * @param in Input buffer - * @return Integer - */ - public static int readReverseInt(IoBuffer in) { - final byte a = in.get(); - final byte b = in.get(); - final byte c = in.get(); - final byte d = in.get(); - int val = 0; - val += (d & 0xff) << 24; - val += (c & 0xff) << 16; - val += (b & 0xff) << 8; - val += (a & 0xff); - return val; - } - - /** - * Encodes header size marker and channel id into header marker. - * @param out output buffer - * - * @param headerSize Header size marker - * @param channelId Channel used - */ - public static void encodeHeaderByte(IoBuffer out, byte headerSize, int channelId) { - if (channelId <= 63) { - out.put((byte) ((headerSize << 6) + channelId)); - } else if (channelId <= 320) { - out.put((byte) (headerSize << 6)); - out.put((byte) (channelId - 64)); - } else { - out.put((byte) ((headerSize << 6) | 1)); - channelId -= 64; - out.put((byte) (channelId & 0xff)); - out.put((byte) (channelId >> 8)); - } - } - - /** - * Decode channel id. - * - * @param header Header - * @param byteCount byte count - * @return Channel id - */ - public static int decodeChannelId(int header, int byteCount) { - if (byteCount == 1) { - return (header & 0x3f); - } else if (byteCount == 2) { - return 64 + (header & 0xff); - } else { - return 64 + ((header >> 8) & 0xff) + ((header & 0xff) << 8); - } - } - - /** - * Decode header size. - * - * @param header Header byte - * @param byteCount byte count - * @return Header size byte - */ - public static byte decodeHeaderSize(int header, int byteCount) { - if (byteCount == 1) { - return (byte) (header >> 6); - } else if (byteCount == 2) { - return (byte) (header >> 14); - } else { - return (byte) (header >> 22); - } - } - - /** - * Return header length from marker value. - * - * @param headerSize Header size marker value - * @return Header length - */ - public static int getHeaderLength(byte headerSize) { - switch (headerSize) { - case HEADER_NEW: - return 12; - case HEADER_SAME_SOURCE: - return 8; - case HEADER_TIMER_CHANGE: - return 4; - case HEADER_CONTINUE: - return 1; - default: - return -1; - } - } - - /** - * Compares two RTMP time stamps, accounting for time stamp wrapping. - * - * @param a First time stamp - * @param b Second time stamp - * @return -1 if a < b, 1 if a > b, or 0 if a == b - */ - public static int compareTimestamps(final int a, final int b) { - long diff = diffTimestamps(a, b); - return diff < 0 ? -1 : (diff > 0 ? 1 : 0); - } - - /** - * Calculates the delta between two time stamps, adjusting - * for time stamp wrapping. - * - * @param a First time stamp - * @param b Second time stamp - * @return the distance between a and b, which will be negative if a is less than b. - */ - public static long diffTimestamps(final int a, final int b) { - // first convert each to unsigned integers - final long unsignedA = a & 0xFFFFFFFFL; - final long unsignedB = b & 0xFFFFFFFFL; - // then find the delta - final long delta = unsignedA-unsignedB; - return delta; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/ReceivedMessageTask.java b/src/main/java/org/red5/server/net/rtmp/ReceivedMessageTask.java deleted file mode 100644 index ae5d5b20f..000000000 --- a/src/main/java/org/red5/server/net/rtmp/ReceivedMessageTask.java +++ /dev/null @@ -1,153 +0,0 @@ -package org.red5.server.net.rtmp; - -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.red5.server.api.Red5; -import org.red5.server.net.rtmp.message.Packet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class ReceivedMessageTask implements Callable { - - private final static Logger log = LoggerFactory.getLogger(ReceivedMessageTask.class); - - private RTMPConnection conn; - - private final IRTMPHandler handler; - - private final String sessionId; - - private Packet message; - - // flag representing handling status - private AtomicBoolean done = new AtomicBoolean(false); - - // deadlock guard instance - private DeadlockGuard guard; - - // maximum time allowed to process received message - private long maxHandlingTime = 500L; - - public ReceivedMessageTask(String sessionId, Packet message, IRTMPHandler handler) { - this(sessionId, message, handler, (RTMPConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId)); - } - - public ReceivedMessageTask(String sessionId, Packet message, IRTMPHandler handler, RTMPConnection conn) { - this.sessionId = sessionId; - this.message = message; - this.handler = handler; - this.conn = conn; - } - - public Boolean call() throws Exception { - // set connection to thread local - Red5.setConnectionLocal(conn); - try { - // // don't run the deadlock guard if timeout is <= 0 - if (maxHandlingTime <= 0) { - // don't run the deadlock guard if we're in debug mode - if (!Red5.isDebug()) { - // run a deadlock guard so hanging tasks will be interrupted - guard = new DeadlockGuard(); - new Thread(guard, String.format("DeadlockGuard#%s", sessionId)).start(); - } - } - // pass message to the handler - handler.messageReceived(conn, message); - } catch (Exception e) { - log.error("Error processing received message {} on {}", message, sessionId, e); - } finally { - //log.info("[{}] run end", sessionId); - // clear thread local - Red5.setConnectionLocal(null); - // set done / completed flag - done.set(true); - if (guard != null) { - // calls internal to the deadlock guard to join its thread from this executor task thread - guard.join(); - } - } - return Boolean.valueOf(done.get()); - } - - /** - * Sets maximum handling time for an incoming message. - * - * @param maxHandlingTimeout - */ - public void setMaxHandlingTimeout(long maxHandlingTimeout) { - this.maxHandlingTime = maxHandlingTimeout; - } - - /** - * Prevents deadlocked message handling. - */ - private class DeadlockGuard implements Runnable { - - // executor task thread - private Thread taskThread; - - // deadlock guard thread - private Thread guardThread = null; - - AtomicBoolean sleeping = new AtomicBoolean(false); - - /** - * Creates the deadlock guard to prevent a message task from taking too long to process. - * @param thread - */ - DeadlockGuard() { - // executor thread ref - this.taskThread = Thread.currentThread(); - } - - /** - * Save the reference to the thread, and wait until the maxHandlingTimeout has elapsed. - * If it elapsed, kill the other thread. - * */ - public void run() { - try { - this.guardThread = Thread.currentThread(); - if (log.isDebugEnabled()) { - log.debug("Threads - task: {} guard: {}", taskThread.getName(), guardThread.getName()); - } - sleeping.compareAndSet(false, true); - Thread.sleep(maxHandlingTime); - } catch (InterruptedException e) { - log.debug("Deadlock guard interrupted on {} during sleep", sessionId); - } finally { - sleeping.set(false); - } - // if the message task is not yet done interrupt - if (!done.get()) { - // if the task thread hasn't been interrupted check its live-ness - if (!taskThread.isInterrupted()) { - // if the task thread is alive, interrupt it - if (taskThread.isAlive()) { - log.warn("Interrupting unfinished active task on {}", sessionId); - taskThread.interrupt(); - } - } else { - log.debug("Unfinished active task on {} already interrupted", sessionId); - } - } - } - - /** - * Joins the deadlock guard thread. - * It's called when the task finish before the maxHandlingTimeout - */ - public void join() { - // interrupt deadlock guard if sleeping - if (sleeping.get()) { - guardThread.interrupt(); - } - // Wait only a 1/4 of the max handling time - // TDJ: Not really needed to wait guard die to release taskMessage processing - //guardThread.join(maxHandlingTimeout / 4); - } - - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/net/rtmp/codec/IEventDecoder.java b/src/main/java/org/red5/server/net/rtmp/codec/IEventDecoder.java deleted file mode 100644 index d17affabd..000000000 --- a/src/main/java/org/red5/server/net/rtmp/codec/IEventDecoder.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.codec; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.IConnection.Encoding; -import org.red5.server.net.rtmp.event.Aggregate; -import org.red5.server.net.rtmp.event.AudioData; -import org.red5.server.net.rtmp.event.BytesRead; -import org.red5.server.net.rtmp.event.ChunkSize; -import org.red5.server.net.rtmp.event.FlexMessage; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.Ping; -import org.red5.server.net.rtmp.event.Unknown; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.so.ISharedObjectMessage; - -/** - * Event decoder decodes event objects from incoming byte buffer. - */ -public interface IEventDecoder { - - /** - * Decodes event of Unknown type. - * - * @param dataType Data type - * @param in Byte buffer to decode - * @return Unknown event - */ - public abstract Unknown decodeUnknown(byte dataType, IoBuffer in); - - /** - * Decodes chunk size event. - * - * @param in Byte buffer to decode - * @return ChunkSize event - */ - public abstract ChunkSize decodeChunkSize(IoBuffer in); - - /** - * Decodes shared object message event. - * - * @param in Byte buffer to decode - * @return ISharedObjectMessage event - */ - public abstract ISharedObjectMessage decodeSharedObject(IoBuffer in); - - /** - * Decodes shared object message event from AMF3 encoding. - * - * @param in Byte buffer to decode - * @return ISharedObjectMessage event - */ - public abstract ISharedObjectMessage decodeFlexSharedObject(IoBuffer in); - - /** - * Decode a Notify. - * - * @param encoding - * @param in - * @return decoded notify result - */ - public abstract Notify decodeNotify(Encoding encoding, IoBuffer in); - - /** - * Decodes invocation event. - * - * @param encoding - * @param in Byte buffer to decode - * @return Invoke event - */ - public abstract Invoke decodeInvoke(Encoding encoding, IoBuffer in); - - /** - * Decodes ping event. - * - * @param in Byte buffer to decode - * @return Ping event - */ - public abstract Ping decodePing(IoBuffer in); - - /** - * Decodes BytesRead event. - * - * @param in Byte buffer to decode - * @return BytesRead event - */ - public abstract BytesRead decodeBytesRead(IoBuffer in); - - /** - * Decodes the aggregated data. - * - * @param in Byte buffer to decode - * @return Aggregate event - */ - public abstract Aggregate decodeAggregate(IoBuffer in); - - /** - * Decodes audio data event. - * - * @param in Byte buffer to decode - * @return AudioData event - */ - public abstract AudioData decodeAudioData(IoBuffer in); - - /** - * Decodes video data event. - * - * @param in Byte buffer to decode - * @return VideoData event - */ - public abstract VideoData decodeVideoData(IoBuffer in); - - /** - * Decodes Flex message event. - * - * @param in Byte buffer to decode - * @return FlexMessage event - */ - public abstract FlexMessage decodeFlexMessage(IoBuffer in); - -} diff --git a/src/main/java/org/red5/server/net/rtmp/codec/IEventEncoder.java b/src/main/java/org/red5/server/net/rtmp/codec/IEventEncoder.java deleted file mode 100644 index dcc8d654c..000000000 --- a/src/main/java/org/red5/server/net/rtmp/codec/IEventEncoder.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.codec; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.net.rtmp.event.Aggregate; -import org.red5.server.net.rtmp.event.AudioData; -import org.red5.server.net.rtmp.event.BytesRead; -import org.red5.server.net.rtmp.event.ChunkSize; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.Ping; -import org.red5.server.net.rtmp.event.Unknown; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.so.ISharedObjectMessage; - -/** - * Encodes events to byte buffer. - */ -public interface IEventEncoder { - /** - * Encodes Notify event to byte buffer. - * - * @param notify Notify event - * @return Byte buffer - */ - public abstract IoBuffer encodeNotify(Notify notify); - - /** - * Encodes Invoke event to byte buffer. - * - * @param invoke Invoke event - * @return Byte buffer - */ - public abstract IoBuffer encodeInvoke(Invoke invoke); - - /** - * Encodes Ping event to byte buffer. - * - * @param ping Ping event - * @return Byte buffer - */ - public abstract IoBuffer encodePing(Ping ping); - - /** - * Encodes BytesRead event to byte buffer. - * - * @param streamBytesRead BytesRead event - * @return Byte buffer - */ - public abstract IoBuffer encodeBytesRead(BytesRead streamBytesRead); - - /** - * Encodes Aggregate event to byte buffer. - * - * @param aggregate Aggregate event - * @return Byte buffer - */ - public abstract IoBuffer encodeAggregate(Aggregate aggregate); - - /** - * Encodes AudioData event to byte buffer. - * - * @param audioData AudioData event - * @return Byte buffer - */ - public abstract IoBuffer encodeAudioData(AudioData audioData); - - /** - * Encodes VideoData event to byte buffer. - * - * @param videoData VideoData event - * @return Byte buffer - */ - public abstract IoBuffer encodeVideoData(VideoData videoData); - - /** - * Encodes Unknown event to byte buffer. - * - * @param unknown Unknown event - * @return Byte buffer - */ - public abstract IoBuffer encodeUnknown(Unknown unknown); - - /** - * Encodes ChunkSize event to byte buffer. - * - * @param chunkSize ChunkSize event - * @return Byte buffer - */ - public abstract IoBuffer encodeChunkSize(ChunkSize chunkSize); - - /** - * Encodes SharedObjectMessage event to byte buffer. - * - * @param so ISharedObjectMessage event - * @return Byte buffer - */ - public abstract IoBuffer encodeSharedObject(ISharedObjectMessage so); - - /** - * Encodes SharedObjectMessage event to byte buffer using AMF3 encoding. - * - * @param so ISharedObjectMessage event - * @return Byte buffer - */ - public IoBuffer encodeFlexSharedObject(ISharedObjectMessage so); -} diff --git a/src/main/java/org/red5/server/net/rtmp/codec/RTMP.java b/src/main/java/org/red5/server/net/rtmp/codec/RTMP.java deleted file mode 100644 index 266ec76ff..000000000 --- a/src/main/java/org/red5/server/net/rtmp/codec/RTMP.java +++ /dev/null @@ -1,544 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.codec; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.red5.server.api.IConnection.Encoding; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.net.rtmp.message.Packet; - -/** - * RTMP is the RTMP protocol state representation. - */ -public class RTMP { - - public String[] states = { "connect", "handshake", "connected", "error", "disconnecting", "disconnected" }; - - /** - * Connect state - */ - public static final byte STATE_CONNECT = 0x00; - - /** - * Handshake state. Server sends handshake request to client right after connection established. - */ - public static final byte STATE_HANDSHAKE = 0x01; - - /** - * Connected - */ - public static final byte STATE_CONNECTED = 0x02; - - /** - * Error - */ - public static final byte STATE_ERROR = 0x03; - - /** - * In the processing of disconnecting - */ - public static final byte STATE_DISCONNECTING = 0x04; - - /** - * Disconnected - */ - public static final byte STATE_DISCONNECTED = 0x05; - - /** - * Sent the connect message to origin. - */ - public static final byte STATE_EDGE_CONNECT_ORIGIN_SENT = 0x11; - - /** - * Forwarded client's connect call to origin. - */ - public static final byte STATE_ORIGIN_CONNECT_FORWARDED = 0x12; - - /** - * Edge is disconnecting, waiting Origin close connection. - */ - public static final byte STATE_EDGE_DISCONNECTING = 0x13; - - /** - * RTMP state. - */ - private volatile byte state = STATE_CONNECT; - - /** - * Encryption flag. - */ - private boolean encrypted = false; - - /** - * Map for channels, keyed by channel id. - */ - private final ConcurrentMap channels = new ConcurrentHashMap(3, 0.9f, 1); - - /** - * Read chunk size. Packets are read and written chunk-by-chunk. - */ - private int readChunkSize = 128; - - /** - * Write chunk size. Packets are read and written chunk-by-chunk. - */ - private int writeChunkSize = 128; - - /** - * Encoding type for objects. - */ - private Encoding encoding = Encoding.AMF0; - - /** - * Creates RTMP object; essentially for storing session information. - */ - public RTMP() { - } - - /** - * Returns channel information for a given channel id. - * - * @param channelId - * @return channel info - */ - private ChannelInfo getChannelInfo(int channelId) { - ChannelInfo info = channels.putIfAbsent(channelId, new ChannelInfo()); - if (info == null) { - info = channels.get(channelId); - } - return info; - } - - /** - * @return the encrypted - */ - public boolean isEncrypted() { - return encrypted; - } - - /** - * @param encrypted the encrypted to set - */ - public void setEncrypted(boolean encrypted) { - this.encrypted = encrypted; - } - - /** - * Return current state. - * - * @return State - */ - public byte getState() { - return state; - } - - /** - * Releases a packet. - * - * @param packet Packet to release - */ - private void freePacket(Packet packet) { - if (packet != null && packet.getData() != null) { - packet.clearData(); - } - } - - /** - * Releases the channels. - */ - private void freeChannels() { - for (ChannelInfo info : channels.values()) { - freePacket(info.getReadPacket()); - freePacket(info.getWritePacket()); - } - channels.clear(); - } - - /** - * Setter for state. - * - * @param state New state - */ - public void setState(byte state) { - this.state = state; - if (state == STATE_DISCONNECTED) { - freeChannels(); - } - } - - /** - * Setter for last read header. - * - * @param channelId Channel id - * @param header Header - */ - public void setLastReadHeader(int channelId, Header header) { - getChannelInfo(channelId).setReadHeader(header); - } - - /** - * Return last read header for channel. - * - * @param channelId Channel id - * @return Last read header - */ - public Header getLastReadHeader(int channelId) { - return getChannelInfo(channelId).getReadHeader(); - } - - /** - * Setter for last written header. - * - * @param channelId Channel id - * @param header Header - */ - public void setLastWriteHeader(int channelId, Header header) { - getChannelInfo(channelId).setWriteHeader(header); - } - - /** - * Return last written header for channel. - * - * @param channelId Channel id - * @return Last written header - */ - public Header getLastWriteHeader(int channelId) { - return getChannelInfo(channelId).getWriteHeader(); - } - - /** - * Setter for last read packet. - * - * @param channelId Channel id - * @param packet Packet - */ - public void setLastReadPacket(int channelId, Packet packet) { - final ChannelInfo info = getChannelInfo(channelId); - // grab last packet - Packet prevPacket = info.getReadPacket(); - // set new one - info.setReadPacket(packet); - // free the previous packet - freePacket(prevPacket); - } - - /** - * Return last read packet for channel. - * - * @param channelId Channel id - * @return Last read packet for that channel - */ - public Packet getLastReadPacket(int channelId) { - return getChannelInfo(channelId).getReadPacket(); - } - - /** - * Setter for last written packet. - * - * @param channelId Channel id - * @param packet Last written packet - */ - public void setLastWritePacket(int channelId, Packet packet) { - // Disabled to help GC because we currently don't use the write packets - /* - Packet prevPacket = writePackets.put(channelId, packet); - if (prevPacket != null && prevPacket.getData() != null) { - prevPacket.getData().release(); - prevPacket.setData(null); - } - */ - } - - /** - * Return packet that has been written last. - * - * @param channelId Channel id - * @return Packet that has been written last - */ - public Packet getLastWritePacket(int channelId) { - return getChannelInfo(channelId).getWritePacket(); - } - - /** - * Getter for write chunk size. Data is being read chunk-by-chunk. - * - * @return Read chunk size - */ - public int getReadChunkSize() { - return readChunkSize; - } - - /** - * Setter for read chunk size. Data is being read chunk-by-chunk. - * - * @param readChunkSize Value to set for property 'readChunkSize'. - */ - public void setReadChunkSize(int readChunkSize) { - this.readChunkSize = readChunkSize; - } - - /** - * Getter for write chunk size. Data is being written chunk-by-chunk. - * - * @return Write chunk size - */ - public int getWriteChunkSize() { - return writeChunkSize; - } - - /** - * Setter for write chunk size. - * - * @param writeChunkSize Write chunk size - */ - public void setWriteChunkSize(int writeChunkSize) { - this.writeChunkSize = writeChunkSize; - } - - /** - * Getter for encoding version. - * - * @return Encoding version - */ - public Encoding getEncoding() { - return encoding; - } - - /** - * Setter for encoding version. - * - * @param encoding Encoding version - */ - public void setEncoding(Encoding encoding) { - this.encoding = encoding; - } - - public void setLastFullTimestampWritten(int channelId, int timer) { - getChannelInfo(channelId).setWriteTimestamp(timer); - } - - public int getLastFullTimestampWritten(int channelId) { - return getChannelInfo(channelId).getWriteTimestamp(); - } - - public void setLastReadPacketHeader(int channelId, Header header) { - getChannelInfo(channelId).setReadPacketHeader(header); - } - - public Header getLastReadPacketHeader(int channelId) { - return getChannelInfo(channelId).getReadPacketHeader(); - } - - LiveTimestampMapping getLastTimestampMapping(int channelId) { - return getChannelInfo(channelId).getLiveTimestamp(); - } - - void setLastTimestampMapping(int channelId, LiveTimestampMapping mapping) { - getChannelInfo(channelId).setLiveTimestamp(mapping); - } - - void clearLastTimestampMapping(int... channelIds) { - for (int channelId : channelIds) { - getChannelInfo(channelId).setLiveTimestamp(null); - } - } - - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "RTMP [state=" + states[state] + ", encrypted=" + encrypted + ", readChunkSize=" + readChunkSize + ", writeChunkSize=" + writeChunkSize + ", encoding=" + encoding - + "]"; - } - - /** - * Class for mapping between clock time and stream time for live streams - */ - final class LiveTimestampMapping { - - private final long clockStartTime; - - private final long streamStartTime; - - private boolean keyFrameNeeded; - - private long lastStreamTime; - - public LiveTimestampMapping(long clockStartTime, long streamStartTime) { - this.clockStartTime = clockStartTime; - this.streamStartTime = streamStartTime; - this.keyFrameNeeded = true; // Always start with a key frame - this.lastStreamTime = streamStartTime; - } - - public long getStreamStartTime() { - return streamStartTime; - } - - public long getClockStartTime() { - return clockStartTime; - } - - public void setKeyFrameNeeded(boolean keyFrameNeeded) { - this.keyFrameNeeded = keyFrameNeeded; - } - - public boolean isKeyFrameNeeded() { - return keyFrameNeeded; - } - - public long getLastStreamTime() { - return lastStreamTime; - } - - public void setLastStreamTime(long lastStreamTime) { - this.lastStreamTime = lastStreamTime; - } - } - - /** - * Channel details - */ - private final class ChannelInfo { - // read header - private Header readHeader; - - // write header - private Header writeHeader; - - // packet header - private Header readPacketHeader; - - // read packet - private Packet readPacket; - - // written packet - private Packet writePacket; - - // written timestamp - private int writeTimestamp; - - // used for live streams - private LiveTimestampMapping liveTimestamp; - - /** - * @return the readHeader - */ - public Header getReadHeader() { - return readHeader; - } - - /** - * @param readHeader the readHeader to set - */ - public void setReadHeader(Header readHeader) { - this.readHeader = readHeader; - } - - /** - * @return the writeHeader - */ - public Header getWriteHeader() { - return writeHeader; - } - - /** - * @param writeHeader the writeHeader to set - */ - public void setWriteHeader(Header writeHeader) { - this.writeHeader = writeHeader; - } - - /** - * @return the readPacketHeader - */ - public Header getReadPacketHeader() { - return readPacketHeader; - } - - /** - * @param readPacketHeader the readPacketHeader to set - */ - public void setReadPacketHeader(Header readPacketHeader) { - this.readPacketHeader = readPacketHeader; - } - - /** - * @return the readPacket - */ - public Packet getReadPacket() { - return readPacket; - } - - /** - * @param readPacket the readPacket to set - */ - public void setReadPacket(Packet readPacket) { - this.readPacket = readPacket; - } - - /** - * @return the writePacket - */ - public Packet getWritePacket() { - return writePacket; - } - - /** - * @param writePacket the writePacket to set - */ - @SuppressWarnings("unused") - public void setWritePacket(Packet writePacket) { - this.writePacket = writePacket; - } - - /** - * @return the writeTimestamp - */ - public int getWriteTimestamp() { - return writeTimestamp; - } - - /** - * @param writeTimestamp the writeTimestamp to set - */ - public void setWriteTimestamp(int writeTimestamp) { - this.writeTimestamp = writeTimestamp; - } - - /** - * @return the liveTimestamp - */ - public LiveTimestampMapping getLiveTimestamp() { - return liveTimestamp; - } - - /** - * @param liveTimestamp the liveTimestamp to set - */ - public void setLiveTimestamp(LiveTimestampMapping liveTimestamp) { - this.liveTimestamp = liveTimestamp; - } - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/codec/RTMPCodecFactory.java b/src/main/java/org/red5/server/net/rtmp/codec/RTMPCodecFactory.java deleted file mode 100644 index 5165c9ad5..000000000 --- a/src/main/java/org/red5/server/net/rtmp/codec/RTMPCodecFactory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.codec; - -import org.apache.mina.core.session.IoSession; -import org.apache.mina.filter.codec.ProtocolCodecFactory; -import org.apache.mina.filter.codec.ProtocolDecoder; -import org.apache.mina.filter.codec.ProtocolEncoder; - -/** - * RTMP codec factory creates RTMP encoders/decoders. - */ -public class RTMPCodecFactory implements ProtocolCodecFactory { - - /** - * Mina protocol decoder for RTMP. - */ - private RTMPMinaProtocolDecoder decoder; - - /** - * Mina protocol encoder for RTMP. - */ - private RTMPMinaProtocolEncoder encoder; - - /** - * Initialization - */ - public void init() { - decoder = new RTMPMinaProtocolDecoder(); - encoder = new RTMPMinaProtocolEncoder(); - } - - /** {@inheritDoc} */ - public ProtocolDecoder getDecoder(IoSession session) { - return decoder; - } - - /** {@inheritDoc} */ - public ProtocolEncoder getEncoder(IoSession session) { - return encoder; - } - - /** - * Returns the RTMP decoder. - * - * @return decoder - */ - public RTMPProtocolDecoder getRTMPDecoder() { - return decoder.getDecoder(); - } - - /** - * Returns the RTMP encoder. - * - * @return encoder - */ - public RTMPProtocolEncoder getRTMPEncoder() { - return encoder.getEncoder(); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/codec/RTMPMinaProtocolDecoder.java b/src/main/java/org/red5/server/net/rtmp/codec/RTMPMinaProtocolDecoder.java deleted file mode 100644 index e0da0e1cb..000000000 --- a/src/main/java/org/red5/server/net/rtmp/codec/RTMPMinaProtocolDecoder.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.codec; - -import java.util.List; -import java.util.concurrent.Semaphore; - -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.session.IoSession; -import org.apache.mina.filter.codec.ProtocolCodecException; -import org.apache.mina.filter.codec.ProtocolDecoderAdapter; -import org.apache.mina.filter.codec.ProtocolDecoderOutput; -import org.red5.server.api.Red5; -import org.red5.server.net.rtmp.RTMPConnManager; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.net.rtmp.message.Constants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RTMP protocol decoder. - */ -public class RTMPMinaProtocolDecoder extends ProtocolDecoderAdapter { - - protected static Logger log = LoggerFactory.getLogger(RTMPMinaProtocolDecoder.class); - - private RTMPProtocolDecoder decoder = new RTMPProtocolDecoder(); - - /** {@inheritDoc} */ - public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws ProtocolCodecException { - // create a buffer and store it on the session - IoBuffer buf = (IoBuffer) session.getAttribute("buffer"); - if (buf == null) { - buf = IoBuffer.allocate(Constants.HANDSHAKE_SIZE); - buf.setAutoExpand(true); - session.setAttribute("buffer", buf); - } - buf.put(in); - buf.flip(); - // get the connection from the session - String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - log.trace("Session id: {}", sessionId); - // connection verification routine - RTMPConnection conn = (RTMPConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); - RTMPConnection connLocal = (RTMPConnection) Red5.getConnectionLocal(); - if (connLocal == null || !conn.getSessionId().equals(connLocal.getSessionId())) { - if (log.isDebugEnabled() && connLocal != null) { - log.debug("Connection local didn't match session"); - } - } - // set the connection to local if its referred to by this session - Red5.setConnectionLocal(conn); - // get the connections decoder lock - final Semaphore lock = conn.getDecoderLock(); - try { - // acquire the decoder lock - log.trace("Decoder lock acquiring.. {}", conn.getSessionId()); - lock.acquire(); - log.trace("Decoder lock acquired {}", conn.getSessionId()); - // construct any objects from the decoded bugger - List objects = decoder.decodeBuffer(conn, buf); - if (objects != null) { - for (Object object : objects) { - out.write(object); - } - } - } catch (Exception e) { - log.error("Error during decode", e); - } finally { - log.trace("Decoder lock releasing.. {}", conn.getSessionId()); - lock.release(); - // clear local - Red5.setConnectionLocal(null); - } - } - - /** - * Sets the RTMP protocol decoder. - * - * @param decoder - */ - public void setDecoder(RTMPProtocolDecoder decoder) { - this.decoder = decoder; - } - - /** - * Returns an RTMP decoder - * @return RTMP decoder - */ - public RTMPProtocolDecoder getDecoder() { - return decoder; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/codec/RTMPMinaProtocolEncoder.java b/src/main/java/org/red5/server/net/rtmp/codec/RTMPMinaProtocolEncoder.java deleted file mode 100644 index ca94d8dd6..000000000 --- a/src/main/java/org/red5/server/net/rtmp/codec/RTMPMinaProtocolEncoder.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.codec; - -import java.util.LinkedList; -import java.util.concurrent.Semaphore; - -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.session.IoSession; -import org.apache.mina.filter.codec.ProtocolCodecException; -import org.apache.mina.filter.codec.ProtocolEncoderAdapter; -import org.apache.mina.filter.codec.ProtocolEncoderOutput; -import org.red5.server.api.Red5; -import org.red5.server.net.rtmp.RTMPConnManager; -import org.red5.server.net.rtmp.RTMPConnection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Mina protocol encoder for RTMP. - */ -public class RTMPMinaProtocolEncoder extends ProtocolEncoderAdapter { - - protected static Logger log = LoggerFactory.getLogger(RTMPMinaProtocolEncoder.class); - - private RTMPProtocolEncoder encoder = new RTMPProtocolEncoder(); - - private int targetChunkSize = 2048; - - /** {@inheritDoc} */ - public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws ProtocolCodecException { - // get the connection from the session - String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - log.trace("Session id: {}", sessionId); - RTMPConnection conn = (RTMPConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); - if (conn != null) { - // look for and compare the connection local; set it from the session - if (!conn.equals((RTMPConnection) Red5.getConnectionLocal())) { - log.debug("Connection local ({}) didn't match io session ({})", (Red5.getConnectionLocal() != null ? Red5.getConnectionLocal().getSessionId() : "null"), sessionId); - Red5.setConnectionLocal(conn); - } - final Semaphore lock = conn.getEncoderLock(); - try { - // acquire the decoder lock - log.trace("Encoder lock acquiring.. {}", conn.getSessionId()); - lock.acquire(); - log.trace("Encoder lock acquired {}", conn.getSessionId()); - // get the buffer - final IoBuffer buf = message instanceof IoBuffer ? (IoBuffer) message : encoder.encode(message); - if (buf != null) { - int requestedWriteChunkSize = conn.getState().getWriteChunkSize(); - log.trace("Requested chunk size: {} target chunk size: {}", requestedWriteChunkSize, targetChunkSize); - if (buf.remaining() <= targetChunkSize * 2) { - log.trace("Writing output data"); - out.write(buf); - } else { - /* - LinkedList chunks = Chunker.chunk(buf, requestedWriteChunkSize, targetChunkSize); - log.trace("Writing output data in {} chunks", chunks.size()); - for (IoBuffer chunk : chunks) { - out.write(chunk); - } - chunks.clear(); - chunks = null; - */ - int sentChunks = Chunker.chunkAndWrite(out, buf, requestedWriteChunkSize, targetChunkSize); - log.trace("Wrote {} chunks", sentChunks); - } - } else { - log.trace("Response buffer was null after encoding"); - } - // WriteFuture future = out.flush(); - // if (future != null) { - // future.addListener(new IoFutureListener() { - // @Override - // public void operationComplete(WriteFuture future) { - // //log.debug("Buffer freed"); - // buf.free(); - // } - // }); - // } - } catch (Exception ex) { - log.error("Exception during encode", ex); - } finally { - log.trace("Encoder lock releasing.. {}", conn.getSessionId()); - lock.release(); - } - } else { - log.debug("Connection is no longer available for encoding, may have been closed already"); - } - } - - /** - * Sets an RTMP protocol encoder - * @param encoder the RTMP encoder - */ - public void setEncoder(RTMPProtocolEncoder encoder) { - this.encoder = encoder; - } - - /** - * Returns an RTMP encoder - * @return RTMP encoder - */ - public RTMPProtocolEncoder getEncoder() { - return encoder; - } - - /** - * Setter for baseTolerance - * */ - public void setBaseTolerance(long baseTolerance) { - encoder.setBaseTolerance(baseTolerance); - } - - /** - * Setter for dropLiveFuture - * */ - public void setDropLiveFuture(boolean dropLiveFuture) { - encoder.setDropLiveFuture(dropLiveFuture); - } - - /** - * @return the targetChunkSize - */ - public int getTargetChunkSize() { - return targetChunkSize; - } - - /** - * @param targetChunkSize the targetChunkSize to set - */ - public void setTargetChunkSize(int targetChunkSize) { - this.targetChunkSize = targetChunkSize; - } - - /** - * Output data chunker. - */ - private static final class Chunker { - - @SuppressWarnings("unused") - public static LinkedList chunk(IoBuffer message, int chunkSize, int desiredSize) { - LinkedList chunks = new LinkedList(); - int targetSize = desiredSize > chunkSize ? desiredSize : chunkSize; - int limit = message.limit(); - do { - int length = 0; - int pos = message.position(); - while (length < targetSize && pos < limit) { - byte basicHeader = message.get(pos); - length += getDataSize(basicHeader) + chunkSize; - pos += length; - } - int remaining = message.remaining(); - log.trace("Length: {} remaining: {} pos+len: {} limit: {}", new Object[] { length, remaining, (message.position() + length), limit }); - if (length > remaining) { - length = remaining; - } - // add a chunk - chunks.add(message.getSlice(length)); - } while (message.hasRemaining()); - return chunks; - } - - public static int chunkAndWrite(ProtocolEncoderOutput out, IoBuffer message, int chunkSize, int desiredSize) { - int sentChunks = 0; - int targetSize = desiredSize > chunkSize ? desiredSize : chunkSize; - int limit = message.limit(); - do { - int length = 0; - int pos = message.position(); - while (length < targetSize && pos < limit) { - byte basicHeader = message.get(pos); - length += getDataSize(basicHeader) + chunkSize; - pos += length; - } - int remaining = message.remaining(); - log.trace("Length: {} remaining: {} pos+len: {} limit: {}", new Object[] { length, remaining, (message.position() + length), limit }); - if (length > remaining) { - length = remaining; - } - // send it - out.write(message.getSlice(length)); - sentChunks++; - } while (message.hasRemaining()); - return sentChunks; - } - - private static int getDataSize(byte basicHeader) { - final int streamId = basicHeader & 0x0000003F; - final int headerType = (basicHeader >> 6) & 0x00000003; - int size = 0; - switch (headerType) { - case 0: - size = 12; - break; - case 1: - size = 8; - break; - case 2: - size = 4; - break; - default: - size = 1; - break; - } - if (streamId == 0) { - size += 1; - } else if (streamId == 1) { - size += 2; - } - return size; - } - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java b/src/main/java/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java deleted file mode 100644 index 54c1b2569..000000000 --- a/src/main/java/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java +++ /dev/null @@ -1,1173 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.codec; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.io.amf.AMF; -import org.red5.io.amf.Output; -import org.red5.io.amf3.AMF3; -import org.red5.io.object.DataTypes; -import org.red5.io.object.Deserializer; -import org.red5.io.object.Input; -import org.red5.io.object.StreamAction; -import org.red5.io.utils.BufferUtils; -import org.red5.server.api.IConnection.Encoding; -import org.red5.server.api.Red5; -import org.red5.server.net.protocol.HandshakeFailedException; -import org.red5.server.net.protocol.ProtocolException; -import org.red5.server.net.protocol.RTMPDecodeState; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.net.rtmp.RTMPUtils; -import org.red5.server.net.rtmp.event.Abort; -import org.red5.server.net.rtmp.event.Aggregate; -import org.red5.server.net.rtmp.event.AudioData; -import org.red5.server.net.rtmp.event.BytesRead; -import org.red5.server.net.rtmp.event.ChunkSize; -import org.red5.server.net.rtmp.event.ClientBW; -import org.red5.server.net.rtmp.event.FlexMessage; -import org.red5.server.net.rtmp.event.FlexStreamSend; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.Ping; -import org.red5.server.net.rtmp.event.SWFResponse; -import org.red5.server.net.rtmp.event.ServerBW; -import org.red5.server.net.rtmp.event.SetBuffer; -import org.red5.server.net.rtmp.event.Unknown; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.net.rtmp.message.Packet; -import org.red5.server.net.rtmp.message.SharedObjectTypeMapping; -import org.red5.server.service.Call; -import org.red5.server.service.PendingCall; -import org.red5.server.so.FlexSharedObjectMessage; -import org.red5.server.so.ISharedObjectEvent; -import org.red5.server.so.ISharedObjectMessage; -import org.red5.server.so.SharedObjectMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RTMP protocol decoder. - */ -public class RTMPProtocolDecoder implements Constants, IEventDecoder { - - protected static Logger log = LoggerFactory.getLogger(RTMPProtocolDecoder.class); - - /** Constructs a new RTMPProtocolDecoder. */ - public RTMPProtocolDecoder() { - } - - /** - * Decode all available objects in buffer. - * - * @param conn RTMP connection - * @param buffer IoBuffer of data to be decoded - * @return a list of decoded objects, may be empty if nothing could be decoded - */ - public List decodeBuffer(RTMPConnection conn, IoBuffer buffer) { - // decoded results - List result = null; - if (conn != null) { - log.trace("Decoding for connection - session id: {}", conn.getSessionId()); - try { - // instance list to hold results - result = new LinkedList(); - // get the local decode state - RTMPDecodeState state = conn.getDecoderState(); - log.trace("{}", state); - if (!conn.getSessionId().equals(state.getSessionId())) { - log.warn("Session decode overlap: {} != {}", conn.getSessionId(), state.getSessionId()); - } - while (buffer.hasRemaining()) { - final int remaining = buffer.remaining(); - if (state.canStartDecoding(remaining)) { - log.trace("Can start decoding"); - state.startDecoding(); - } else { - log.trace("Cannot start decoding"); - break; - } - final Object decodedObject = decode(conn, state, buffer); - if (state.hasDecodedObject()) { - log.trace("Has decoded object"); - if (decodedObject != null) { - result.add(decodedObject); - } - } else if (state.canContinueDecoding()) { - log.trace("Can continue decoding"); - continue; - } else { - log.trace("Cannot continue decoding"); - break; - } - } - } catch (HandshakeFailedException hfe) { - // close the connection - log.warn("Closing connection because decoding failed during handshake: {}", conn, hfe); - // clear buffer if something is wrong in protocol decoding - buffer.clear(); - // close connection because we can't parse data from it - conn.close(); - } catch (Exception ex) { - // catch any non-handshake exception in the decoding - // close the connection - log.warn("Closing connection because decoding failed: {}", conn, ex); - // clear the buffer to eliminate memory leaks when we can't parse protocol - buffer.clear(); - // close connection because we can't parse data from it - conn.close(); - } finally { - buffer.compact(); - } - } else { - log.error("Decoding buffer failed, no current connection!?"); - } - return result; - } - - /** - * Decodes the buffer data. - * - * @param conn RTMP connection - * @param state Stores state for the protocol, ProtocolState is just a marker interface - * @param in IoBuffer of data to be decoded - * @return one of three possible values: - * 1. null : the object could not be decoded, or some data was skipped, just continue - * 2. ProtocolState : the decoder was unable to decode the whole object, refer to the protocol state - * 3. Object : something was decoded, continue - * @throws Exception on error - */ - public Object decode(RTMPConnection conn, RTMPDecodeState state, IoBuffer in) throws ProtocolException { - if (log.isTraceEnabled()) { - log.trace("Decoding for {}", conn.getSessionId()); - } - try { - final byte connectionState = conn.getStateCode(); - switch (connectionState) { - case RTMP.STATE_CONNECTED: - return decodePacket(conn, state, in); - case RTMP.STATE_CONNECT: - return decodeHandshakeS1(conn, state, in); - case RTMP.STATE_HANDSHAKE: - return decodeHandshakeS2(conn, state, in); - case RTMP.STATE_ERROR: - case RTMP.STATE_DISCONNECTING: - case RTMP.STATE_DISCONNECTED: - // throw away any remaining input data: - in.position(in.limit()); - return null; - default: - throw new IllegalStateException("Invalid RTMP state: " + connectionState); - } - } catch (ProtocolException pe) { - // raise to caller unmodified - throw pe; - } catch (RuntimeException e) { - throw new ProtocolException("Error during decoding", e); - } finally { - if (log.isTraceEnabled()) { - log.trace("Decoding finished for {}", conn.getSessionId()); - } - } - } - - /** - * Decodes handshake message for step 1, RTMP.STATE_CONNECT. - * - * @param conn Connection - * @param state protocol decode state - * @param in IoBuffer - * @return IoBuffer - */ - public IoBuffer decodeHandshakeS1(RTMPConnection conn, RTMPDecodeState state, IoBuffer in) { - // first step: client has connected and handshaking is not complete - if (log.isDebugEnabled()) { - log.debug("decodeHandshake - state: {} buffer: {}", state, in); - } - // number of byte remaining in the buffer - int remaining = in.remaining(); - if (remaining < HANDSHAKE_SIZE + 1) { - log.debug("Handshake init too small, buffering. remaining: {}", remaining); - state.bufferDecoding(HANDSHAKE_SIZE + 1); - } else { - final IoBuffer hs = IoBuffer.allocate(HANDSHAKE_SIZE); - in.get(); // skip the header byte - BufferUtils.put(hs, in, HANDSHAKE_SIZE); - hs.flip(); - conn.setStateCode(RTMP.STATE_HANDSHAKE); - return hs; - } - return null; - } - - /** - * Decodes handshake message for step 2, RTMP.STATE_HANDSHAKE. - * - * @param conn Connection - * @param state protocol decode state - * @param in IoBuffer - * @return IoBuffer - */ - public IoBuffer decodeHandshakeS2(RTMPConnection conn, RTMPDecodeState state, IoBuffer in) { - // second step: all handshake data received, collecting handshake reply data - log.debug("decodeHandshake - state: {} buffer: {}", state, in); - // number of byte remaining in the buffer - int remaining = in.remaining(); - // TODO Paul: re-examine how remaining data is buffered between handshake reply and next message when using rtmpe - // connections sending partial tcp are getting dropped - log.debug("Handshake reply"); - // how many bytes left to get - int required = state.getDecoderBufferAmount(); - log.trace("Handshake reply - required: {} remaining: {}", required, remaining); - if (remaining < HANDSHAKE_SIZE) { - log.debug("Handshake reply too small, buffering. remaining: {}", remaining); - state.bufferDecoding(HANDSHAKE_SIZE); - } else { - in.skip(HANDSHAKE_SIZE); - conn.setStateCode(RTMP.STATE_CONNECTED); - state.continueDecoding(); - } - return null; - } - - /** - * Decodes an IoBuffer into a Packet. - * - * @param conn Connection - * @param state RTMP protocol state - * @param in IoBuffer - * @return Packet - */ - public Packet decodePacket(RTMPConnection conn, RTMPDecodeState state, IoBuffer in) { - if (log.isTraceEnabled()) { - log.trace("decodePacket - state: {} buffer: {}", state, in); - } - final int remaining = in.remaining(); - // We need at least one byte - if (remaining < 1) { - state.bufferDecoding(1); - return null; - } - final int position = in.position(); - byte headerByte = in.get(); - int headerValue; - int byteCount; - if ((headerByte & 0x3f) == 0) { - // Two byte header - if (remaining < 2) { - in.position(position); - state.bufferDecoding(2); - return null; - } - headerValue = (headerByte & 0xff) << 8 | (in.get() & 0xff); - byteCount = 2; - } else if ((headerByte & 0x3f) == 1) { - // Three byte header - if (remaining < 3) { - in.position(position); - state.bufferDecoding(3); - return null; - } - headerValue = (headerByte & 0xff) << 16 | (in.get() & 0xff) << 8 | (in.get() & 0xff); - byteCount = 3; - } else { - // Single byte header - headerValue = headerByte & 0xff; - byteCount = 1; - } - final int channelId = RTMPUtils.decodeChannelId(headerValue, byteCount); - if (channelId < 0) { - throw new ProtocolException("Bad channel id: " + channelId); - } - RTMP rtmp = conn.getState(); - // Get the header size and length - byte headerSize = RTMPUtils.decodeHeaderSize(headerValue, byteCount); - int headerLength = RTMPUtils.getHeaderLength(headerSize); - Header lastHeader = rtmp.getLastReadHeader(channelId); - headerLength += byteCount - 1; - switch (headerSize) { - case HEADER_NEW: - case HEADER_SAME_SOURCE: - case HEADER_TIMER_CHANGE: - if (remaining >= headerLength) { - int timeValue = RTMPUtils.readUnsignedMediumInt(in); - if (timeValue == 0xffffff) { - headerLength += 4; - } - } - break; - case HEADER_CONTINUE: - if (lastHeader != null && lastHeader.getExtendedTimestamp() != 0) { - headerLength += 4; - } - break; - default: - throw new ProtocolException("Unexpected header size " + headerSize + " check for error"); - } - if (remaining < headerLength) { - log.trace("Header too small (hlen: {}), buffering. remaining: {}", headerLength, remaining); - in.position(position); - state.bufferDecoding(headerLength); - return null; - } - // Move the position back to the start - in.position(position); - final Header header = decodeHeader(in, lastHeader); - if (header == null) { - throw new ProtocolException("Header is null, check for error"); - } - rtmp.setLastReadHeader(channelId, header); - // check to see if this is a new packets or continue decoding an existing one - Packet packet = rtmp.getLastReadPacket(channelId); - if (packet == null) { - packet = new Packet(header.clone()); - rtmp.setLastReadPacket(channelId, packet); - } - final IoBuffer buf = packet.getData(); - final int readRemaining = header.getSize() - buf.position(); - final int chunkSize = rtmp.getReadChunkSize(); - final int readAmount = (readRemaining > chunkSize) ? chunkSize : readRemaining; - if (in.remaining() < readAmount) { - log.debug("Chunk too small, buffering ({},{})", in.remaining(), readAmount); - // skip the position back to the start - in.position(position); - state.bufferDecoding(headerLength + readAmount); - return null; - } - BufferUtils.put(buf, in, readAmount); - if (buf.position() < header.getSize()) { - state.continueDecoding(); - return null; - } - if (buf.position() > header.getSize()) { - log.warn("Packet size expanded from {} to {} ({})", new Object[] { (header.getSize()), buf.position(), header }); - } - buf.flip(); - try { - final IRTMPEvent message = decodeMessage(conn, packet.getHeader(), buf); - message.setHeader(packet.getHeader()); - // Unfortunately flash will, especially when resetting a video stream with a new key frame, sometime - // send an earlier time stamp. To avoid dropping it, we just give it the minimal increment since the - // last message. But to avoid relative time stamps being mis-computed, we don't reset the header we stored. - final Header lastReadHeader = rtmp.getLastReadPacketHeader(channelId); - if (lastReadHeader != null && (message instanceof AudioData || message instanceof VideoData) - && RTMPUtils.compareTimestamps(lastReadHeader.getTimer(), packet.getHeader().getTimer()) >= 0) { - log.trace("Non-monotonically increasing timestamps; type: {}; adjusting to {}; ts: {}; last: {}", new Object[] { header.getDataType(), - lastReadHeader.getTimer() + 1, header.getTimer(), lastReadHeader.getTimer() }); - message.setTimestamp(lastReadHeader.getTimer() + 1); - } else { - message.setTimestamp(header.getTimer()); - } - rtmp.setLastReadPacketHeader(channelId, packet.getHeader()); - packet.setMessage(message); - if (message instanceof ChunkSize) { - ChunkSize chunkSizeMsg = (ChunkSize) message; - rtmp.setReadChunkSize(chunkSizeMsg.getSize()); - } else if (message instanceof Abort) { - log.debug("Abort packet detected"); - // The client is aborting a message; reset the packet - // because the next chunk on that stream will start a new packet. - Abort abort = (Abort) message; - rtmp.setLastReadPacket(abort.getChannelId(), null); - packet = null; - } - // collapse the time stamps on the last packet so that it works right for chunk type 3 later - lastHeader = rtmp.getLastReadHeader(channelId); - lastHeader.setTimerBase(header.getTimer()); - } finally { - rtmp.setLastReadPacket(channelId, null); - } - return packet; - } - - /** - * Decodes packet header. - * - * @param in Input IoBuffer - * @param lastHeader Previous header - * @return Decoded header - */ - public Header decodeHeader(IoBuffer in, Header lastHeader) { - if (log.isTraceEnabled()) { - log.trace("decodeHeader - lastHeader: {} buffer: {}", lastHeader, in); - } - byte headerByte = in.get(); - int headerValue; - int byteCount = 1; - if ((headerByte & 0x3f) == 0) { - // Two byte header - headerValue = (headerByte & 0xff) << 8 | (in.get() & 0xff); - byteCount = 2; - } else if ((headerByte & 0x3f) == 1) { - // Three byte header - headerValue = (headerByte & 0xff) << 16 | (in.get() & 0xff) << 8 | (in.get() & 0xff); - byteCount = 3; - } else { - // Single byte header - headerValue = headerByte & 0xff; - byteCount = 1; - } - final int channelId = RTMPUtils.decodeChannelId(headerValue, byteCount); - final int headerSize = RTMPUtils.decodeHeaderSize(headerValue, byteCount); - Header header = new Header(); - header.setChannelId(channelId); - if (headerSize != HEADER_NEW && lastHeader == null) { - log.error("Last header null not new, headerSize: {}, channelId {}", headerSize, channelId); - //this will trigger an error status, which in turn will disconnect the "offending" flash player - //preventing a memory leak and bringing the whole server to its knees - return null; - } - int timeValue; - switch (headerSize) { - case HEADER_NEW: - // an absolute time value - timeValue = RTMPUtils.readUnsignedMediumInt(in); - header.setSize(RTMPUtils.readUnsignedMediumInt(in)); - header.setDataType(in.get()); - header.setStreamId(RTMPUtils.readReverseInt(in)); - if (timeValue == 0xffffff) { - timeValue = (int) (in.getUnsignedInt() & Integer.MAX_VALUE); - header.setExtendedTimestamp(timeValue); - } - header.setTimerBase(timeValue); - header.setTimerDelta(0); - break; - case HEADER_SAME_SOURCE: - // a delta time value - timeValue = RTMPUtils.readUnsignedMediumInt(in); - header.setSize(RTMPUtils.readUnsignedMediumInt(in)); - header.setDataType(in.get()); - header.setStreamId(lastHeader.getStreamId()); - if (timeValue == 0xffffff) { - timeValue = (int) (in.getUnsignedInt() & Integer.MAX_VALUE); - header.setExtendedTimestamp(timeValue); - } else if (timeValue == 0 && header.getDataType() == TYPE_AUDIO_DATA) { - // header.setIsGarbage(true); - log.trace("Audio with zero delta; setting to garbage; ChannelId: {}; DataType: {}; HeaderSize: {}", new Object[] { header.getChannelId(), header.getDataType(), - headerSize }); - } - header.setTimerBase(lastHeader.getTimerBase()); - header.setTimerDelta(timeValue); - break; - case HEADER_TIMER_CHANGE: - // a delta time value - timeValue = RTMPUtils.readUnsignedMediumInt(in); - header.setSize(lastHeader.getSize()); - header.setDataType(lastHeader.getDataType()); - header.setStreamId(lastHeader.getStreamId()); - if (timeValue == 0xffffff) { - timeValue = (int) (in.getUnsignedInt() & Integer.MAX_VALUE); - header.setExtendedTimestamp(timeValue); - } else if (timeValue == 0 && header.getDataType() == TYPE_AUDIO_DATA) { - // header.setIsGarbage(true); - log.trace("Audio with zero delta; setting to garbage; ChannelId: {}; DataType: {}; HeaderSize: {}", new Object[] { header.getChannelId(), header.getDataType(), - headerSize }); - } - header.setTimerBase(lastHeader.getTimerBase()); - header.setTimerDelta(timeValue); - break; - case HEADER_CONTINUE: - header.setSize(lastHeader.getSize()); - header.setDataType(lastHeader.getDataType()); - header.setStreamId(lastHeader.getStreamId()); - header.setTimerBase(lastHeader.getTimerBase()); - header.setTimerDelta(lastHeader.getTimerDelta()); - if (lastHeader.getExtendedTimestamp() != 0) { - timeValue = (int) (in.getUnsignedInt() & Integer.MAX_VALUE); - header.setExtendedTimestamp(timeValue); - log.trace("HEADER_CONTINUE with extended timestamp: {}", timeValue); - } - break; - default: - log.error("Unexpected header size: {}", headerSize); - return null; - } - log.trace("CHUNK, D, {}, {}", header, headerSize); - return header; - } - - /** - * Decodes RTMP message event. - * - * @param conn RTMP connection - * @param header RTMP header - * @param in Input IoBuffer - * @return RTMP event - */ - public IRTMPEvent decodeMessage(RTMPConnection conn, Header header, IoBuffer in) { - IRTMPEvent message; - byte dataType = header.getDataType(); - switch (dataType) { - case TYPE_INVOKE: - message = decodeInvoke(conn.getEncoding(), in); - break; - case TYPE_NOTIFY: - if (header.getStreamId() == 0) { - message = decodeNotify(conn.getEncoding(), in, header); - } else { - message = decodeStreamMetadata(in); - } - break; - case TYPE_AUDIO_DATA: - message = decodeAudioData(in); - message.setSourceType(Constants.SOURCE_TYPE_LIVE); - break; - case TYPE_VIDEO_DATA: - message = decodeVideoData(in); - message.setSourceType(Constants.SOURCE_TYPE_LIVE); - break; - case TYPE_AGGREGATE: - message = decodeAggregate(in); - break; - case TYPE_FLEX_SHARED_OBJECT: // represents an SO in an AMF3 container - message = decodeFlexSharedObject(in); - break; - case TYPE_SHARED_OBJECT: - message = decodeSharedObject(in); - break; - case TYPE_FLEX_MESSAGE: - message = decodeFlexMessage(in); - break; - case TYPE_FLEX_STREAM_SEND: - message = decodeFlexStreamSend(in); - break; - case TYPE_PING: - message = decodePing(in); - break; - case TYPE_BYTES_READ: - message = decodeBytesRead(in); - break; - case TYPE_CHUNK_SIZE: - message = decodeChunkSize(in); - break; - case TYPE_SERVER_BANDWIDTH: - message = decodeServerBW(in); - break; - case TYPE_CLIENT_BANDWIDTH: - message = decodeClientBW(in); - break; - case TYPE_ABORT: - message = decodeAbort(in); - break; - default: - log.warn("Unknown object type: {}", dataType); - message = decodeUnknown(dataType, in); - break; - } - return message; - } - - public IRTMPEvent decodeAbort(IoBuffer in) { - return new Abort(in.getInt()); - } - - /** - * Decodes server bandwidth. - * - * @param in IoBuffer - * @return RTMP event - */ - private IRTMPEvent decodeServerBW(IoBuffer in) { - return new ServerBW(in.getInt()); - } - - /** - * Decodes client bandwidth. - * - * @param in - * Byte buffer - * @return RTMP event - */ - private IRTMPEvent decodeClientBW(IoBuffer in) { - return new ClientBW(in.getInt(), in.get()); - } - - /** {@inheritDoc} */ - public Unknown decodeUnknown(byte dataType, IoBuffer in) { - return new Unknown(dataType, in); - } - - /** {@inheritDoc} */ - public Aggregate decodeAggregate(IoBuffer in) { - return new Aggregate(in); - } - - /** {@inheritDoc} */ - public ChunkSize decodeChunkSize(IoBuffer in) { - int chunkSize = in.getInt(); - log.debug("Decoded chunk size: {}", chunkSize); - return new ChunkSize(chunkSize); - } - - /** {@inheritDoc} */ - public ISharedObjectMessage decodeFlexSharedObject(IoBuffer in) { - byte encoding = in.get(); - Input input; - if (encoding == 0) { - input = new org.red5.io.amf.Input(in); - } else if (encoding == 3) { - input = new org.red5.io.amf3.Input(in); - } else { - throw new RuntimeException("Unknown SO encoding: " + encoding); - } - String name = input.getString(); - // Read version of SO to modify - int version = in.getInt(); - // Read persistence informations - boolean persistent = in.getInt() == 2; - // Skip unknown bytes - in.skip(4); - // create our shared object message - final SharedObjectMessage so = new FlexSharedObjectMessage(null, name, version, persistent); - doDecodeSharedObject(so, in, input); - return so; - } - - /** {@inheritDoc} */ - public ISharedObjectMessage decodeSharedObject(IoBuffer in) { - final Input input = new org.red5.io.amf.Input(in); - String name = input.getString(); - // Read version of SO to modify - int version = in.getInt(); - // Read persistence informations - boolean persistent = in.getInt() == 2; - // Skip unknown bytes - in.skip(4); - // create our shared object message - final SharedObjectMessage so = new SharedObjectMessage(null, name, version, persistent); - doDecodeSharedObject(so, in, input); - return so; - } - - /** - * Perform the actual decoding of the shared object contents. - * - * @param so - * @param in - * @param input - */ - protected void doDecodeSharedObject(SharedObjectMessage so, IoBuffer in, Input input) { - // Parse request body - Input amf3Input = new org.red5.io.amf3.Input(in); - while (in.hasRemaining()) { - final ISharedObjectEvent.Type type = SharedObjectTypeMapping.toType(in.get()); - if (type == null) { - in.skip(in.remaining()); - return; - } - String key = null; - Object value = null; - - // if (log.isDebugEnabled()) - // log.debug("type: "+SharedObjectTypeMapping.toString(type)); - - // SharedObjectEvent event = new SharedObjectEvent(,null,null); - final int length = in.getInt(); - if (type == ISharedObjectEvent.Type.CLIENT_STATUS) { - // Status code - key = input.getString(); - // Status level - value = input.getString(); - } else if (type == ISharedObjectEvent.Type.CLIENT_UPDATE_DATA) { - key = null; - // Map containing new attribute values - final Map map = new HashMap(); - final int start = in.position(); - while (in.position() - start < length) { - String tmp = input.getString(); - map.put(tmp, Deserializer.deserialize(input, Object.class)); - } - value = map; - } else if (type != ISharedObjectEvent.Type.SERVER_SEND_MESSAGE && type != ISharedObjectEvent.Type.CLIENT_SEND_MESSAGE) { - if (length > 0) { - key = input.getString(); - if (length > key.length() + 2) { - // determine if the object is encoded with amf3 - byte objType = in.get(); - in.position(in.position() - 1); - Input propertyInput; - if (objType == AMF.TYPE_AMF3_OBJECT && !(input instanceof org.red5.io.amf3.Input)) { - // The next parameter is encoded using AMF3 - propertyInput = amf3Input; - } else { - // The next parameter is encoded using AMF0 - propertyInput = input; - } - value = Deserializer.deserialize(propertyInput, Object.class); - } - } - } else { - final int start = in.position(); - // the "send" event seems to encode the handler name as complete AMF string including the string type byte - key = Deserializer.deserialize(input, String.class); - // read parameters - final List list = new LinkedList(); - while (in.position() - start < length) { - byte objType = in.get(); - in.position(in.position() - 1); - // determine if the object is encoded with amf3 - Input propertyInput; - if (objType == AMF.TYPE_AMF3_OBJECT && !(input instanceof org.red5.io.amf3.Input)) { - // The next parameter is encoded using AMF3 - propertyInput = amf3Input; - } else { - // The next parameter is encoded using AMF0 - propertyInput = input; - } - Object tmp = Deserializer.deserialize(propertyInput, Object.class); - list.add(tmp); - } - value = list; - } - so.addEvent(type, key, value); - } - } - - /** {@inheritDoc} */ - public Notify decodeNotify(Encoding encoding, IoBuffer in) { - return decodeNotify(encoding, in, null); - } - - /** - * Decode a Notify. - * - * @param encoding - * @param in - * @param header - * @return decoded notify result - */ - public Notify decodeNotify(Encoding encoding, IoBuffer in, Header header) { - Notify notify = new Notify(); - int start = in.position(); - Input input; - // for response, the action string and invokeId is always encoded as AMF0 we use the first byte to decide which encoding to use - byte tmp = in.get(); - in.position(start); - if (encoding == Encoding.AMF3 && tmp == AMF.TYPE_AMF3_OBJECT) { - input = new org.red5.io.amf3.Input(in); - ((org.red5.io.amf3.Input) input).enforceAMF3(); - } else { - input = new org.red5.io.amf.Input(in); - } - // get the action - String action = Deserializer.deserialize(input, String.class); - if (log.isTraceEnabled()) { - log.trace("Action " + action); - } - //throw a runtime exception if there is no action - if (action != null) { - //TODO Handle NetStream.send? Where and how? - if (header != null && header.getStreamId() != 0 && !isStreamCommand(action)) { - // don't decode "NetStream.send" requests - in.position(start); - notify.setData(in.asReadOnlyBuffer()); - return notify; - } - if (header == null || header.getStreamId() == 0) { - int invokeId = Deserializer. deserialize(input, Number.class).intValue(); - if (invokeId != 0) { - throw new RuntimeException("Notify invoke / transaction id was non-zero"); - } - } - // now go back to the actual encoding to decode parameters - if (encoding == Encoding.AMF3) { - input = new org.red5.io.amf3.Input(in); - ((org.red5.io.amf3.Input) input).enforceAMF3(); - } else { - input = new org.red5.io.amf.Input(in); - } - // get / set the parameters if there any - Object[] params = handleParameters(in, notify, input); - // determine service information - final int dotIndex = action.lastIndexOf('.'); - String serviceName = (dotIndex == -1) ? null : action.substring(0, dotIndex); - // pull off the prefixes since java doesn't allow this on a method name - if (serviceName != null && (serviceName.startsWith("@") || serviceName.startsWith("|"))) { - serviceName = serviceName.substring(1); - } - String serviceMethod = (dotIndex == -1) ? action : action.substring(dotIndex + 1, action.length()); - // pull off the prefixes since java doesnt allow this on a method name - if (serviceMethod.startsWith("@") || serviceMethod.startsWith("|")) { - serviceMethod = serviceMethod.substring(1); - } - Call call = new Call(serviceName, serviceMethod, params); - notify.setCall(call); - return notify; - } else { - //TODO replace this with something better as time permits - throw new RuntimeException("Action was null"); - } - } - - /** {@inheritDoc} */ - public Invoke decodeInvoke(Encoding encoding, IoBuffer in) { - Invoke invoke = new Invoke(); - int start = in.position(); - Input input; - // for response, the action string and invokeId is always encoded as AMF0 we use the first byte to decide which encoding to use. - byte tmp = in.get(); - in.position(start); - if (encoding == Encoding.AMF3 && tmp == AMF.TYPE_AMF3_OBJECT) { - input = new org.red5.io.amf3.Input(in); - ((org.red5.io.amf3.Input) input).enforceAMF3(); - } else { - input = new org.red5.io.amf.Input(in); - } - // get the action - String action = Deserializer.deserialize(input, String.class); - if (log.isTraceEnabled()) { - log.trace("Action " + action); - } - //throw a runtime exception if there is no action - if (action != null) { - invoke.setTransactionId(Deserializer. deserialize(input, Number.class).intValue()); - // now go back to the actual encoding to decode parameters - if (encoding == Encoding.AMF3) { - input = new org.red5.io.amf3.Input(in); - ((org.red5.io.amf3.Input) input).enforceAMF3(); - } else { - input = new org.red5.io.amf.Input(in); - } - // get / set the parameters if there any - Object[] params = handleParameters(in, invoke, input); - // determine service information - final int dotIndex = action.lastIndexOf('.'); - String serviceName = (dotIndex == -1) ? null : action.substring(0, dotIndex); - // pull off the prefixes since java doesnt allow this on a method name - if (serviceName != null && (serviceName.startsWith("@") || serviceName.startsWith("|"))) { - serviceName = serviceName.substring(1); - } - String serviceMethod = (dotIndex == -1) ? action : action.substring(dotIndex + 1, action.length()); - // pull off the prefixes since java doesn't allow this on a method name - if (serviceMethod.startsWith("@") || serviceMethod.startsWith("|")) { - serviceMethod = serviceMethod.substring(1); - } - PendingCall call = new PendingCall(serviceName, serviceMethod, params); - invoke.setCall(call); - return invoke; - } else { - //TODO replace this with something better as time permits - throw new RuntimeException("Action was null"); - } - } - - /** - * Decodes ping event. - * - * @param in IoBuffer - * @return Ping event - */ - public Ping decodePing(IoBuffer in) { - Ping ping = null; - if (log.isTraceEnabled()) { - // gets the raw data as hex without changing the data or pointer - String hexDump = in.getHexDump(); - log.trace("Ping dump: {}", hexDump); - } - // control type - short type = in.getShort(); - switch (type) { - case Ping.CLIENT_BUFFER: - ping = new SetBuffer(in.getInt(), in.getInt()); - break; - case Ping.PING_SWF_VERIFY: - // only contains the type (2 bytes) - ping = new Ping(type); - break; - case Ping.PONG_SWF_VERIFY: - byte[] bytes = new byte[42]; - in.get(bytes); - ping = new SWFResponse(bytes); - break; - default: - //STREAM_BEGIN, STREAM_PLAYBUFFER_CLEAR, STREAM_DRY, RECORDED_STREAM - //PING_CLIENT, PONG_SERVER - //BUFFER_EMPTY, BUFFER_FULL - ping = new Ping(type, in.getInt()); - break; - } - return ping; - } - - /** {@inheritDoc} */ - public BytesRead decodeBytesRead(IoBuffer in) { - return new BytesRead(in.getInt()); - } - - /** {@inheritDoc} */ - public AudioData decodeAudioData(IoBuffer in) { - return new AudioData(in.asReadOnlyBuffer()); - } - - /** {@inheritDoc} */ - public VideoData decodeVideoData(IoBuffer in) { - return new VideoData(in.asReadOnlyBuffer()); - } - - /** - * Decodes stream meta data, to include onMetaData, onCuePoint, and onFI. - * - * @param in - * @return Notify - */ - @SuppressWarnings("unchecked") - public Notify decodeStreamMetadata(IoBuffer in) { - Encoding encoding = ((RTMPConnection) Red5.getConnectionLocal()).getEncoding(); - Input input = null; - - // check to see if the encoding is set to AMF3. - // if it is then check to see if first byte is set to AMF0 - byte amfVersion = 0x00; - if (encoding == Encoding.AMF3) { - amfVersion = in.get(); - } - - // reset the position back to 0 - in.position(0); - - //make a pre-emptive copy of the incoming buffer here to prevent issues that occur fairly often - IoBuffer copy = in.duplicate(); - - - if (encoding == Encoding.AMF0 || amfVersion != AMF.TYPE_AMF3_OBJECT ) { - input = new org.red5.io.amf.Input(copy); - } else { - org.red5.io.amf3.Input.RefStorage refStorage = new org.red5.io.amf3.Input.RefStorage(); - input = new org.red5.io.amf3.Input(copy, refStorage); - } - //get the first datatype - byte dataType = input.readDataType(); - if (dataType == DataTypes.CORE_STRING) { - String setData = input.readString(String.class); - if ("@setDataFrame".equals(setData)) { - // get the second datatype - byte dataType2 = input.readDataType(); - log.debug("Dataframe method type: {}", dataType2); - String onCueOrOnMeta = input.readString(String.class); - // get the params datatype - byte object = input.readDataType(); - log.debug("Dataframe params type: {}", object); - Map params; - if (object == DataTypes.CORE_MAP) { - // the params are sent as a Mixed-Array. Required to support the RTMP publish provided by ffmpeg/xuggler - params = (Map) input.readMap(null); - } else { - // read the params as a standard object - params = (Map) input.readObject(Object.class); - } - log.debug("Dataframe: {} params: {}", onCueOrOnMeta, params.toString()); - - IoBuffer buf = IoBuffer.allocate(1024); - buf.setAutoExpand(true); - Output out = new Output(buf); - out.writeString(onCueOrOnMeta); - out.writeMap(params); - - buf.flip(); - return new Notify(buf); - } else if ("onFI".equals(setData)) { - // the onFI request contains 2 items relative to the publishing client application - // sd = system date (12-07-2011) - // st = system time (09:11:33.387) - byte object = input.readDataType(); - log.debug("onFI params type: {}", object); - Map params; - if (object == DataTypes.CORE_MAP) { - // the params are sent as a Mixed-Array - params = (Map) input.readMap(null); - } else { - // read the params as a standard object - params = (Map) input.readObject(Object.class); - } - log.debug("onFI params: {}", params.toString()); - } else { - log.info("Unhandled request: {}", setData); - if (log.isDebugEnabled()) { - byte object = input.readDataType(); - log.debug("Params type: {}", object); - if (object == DataTypes.CORE_MAP) { - Map params = (Map) input.readMap(null); - log.debug("Params: {}", params.toString()); - } else { - log.debug("The unknown request was did not provide a parameter map"); - } - } - } - } - return new Notify(in.asReadOnlyBuffer()); - } - - /** - * Decodes FlexMessage event. - * - * @param in IoBuffer - * @return FlexMessage event - */ - public FlexMessage decodeFlexMessage(IoBuffer in) { - // TODO: Unknown byte, probably encoding as with Flex SOs? - byte flexByte = in.get(); - log.trace("Flex byte: {}", flexByte); - // Encoding of message params can be mixed - some params may be in AMF0, others in AMF3, - // but according to AMF3 spec, we should collect AMF3 references for the whole message body (through all params) - org.red5.io.amf3.Input.RefStorage refStorage = new org.red5.io.amf3.Input.RefStorage(); - - Input input = new org.red5.io.amf.Input(in); - String action = Deserializer.deserialize(input, String.class); - int transactionId = Deserializer. deserialize(input, Number.class).intValue(); - FlexMessage msg = new FlexMessage(); - msg.setTransactionId(transactionId); - Object[] params = new Object[] {}; - if (in.hasRemaining()) { - ArrayList paramList = new ArrayList(); - final Object obj = Deserializer.deserialize(input, Object.class); - if (obj != null) { - paramList.add(obj); - } - while (in.hasRemaining()) { - // Check for AMF3 encoding of parameters - byte objectEncodingType = in.get(); - in.position(in.position() - 1); - log.debug("Object encoding: {}", objectEncodingType); - switch (objectEncodingType) { - case AMF.TYPE_AMF3_OBJECT: - case AMF3.TYPE_VECTOR_NUMBER: - case AMF3.TYPE_VECTOR_OBJECT: - // The next parameter is encoded using AMF3 - input = new org.red5.io.amf3.Input(in, refStorage); - // Vectors with number and object have to have AMF3 forced - ((org.red5.io.amf3.Input) input).enforceAMF3(); - break; - case AMF3.TYPE_VECTOR_INT: - case AMF3.TYPE_VECTOR_UINT: - // The next parameter is encoded using AMF3 - input = new org.red5.io.amf3.Input(in, refStorage); - break; - default: - // The next parameter is encoded using AMF0 - input = new org.red5.io.amf.Input(in); - } - paramList.add(Deserializer.deserialize(input, Object.class)); - } - params = paramList.toArray(); - if (log.isTraceEnabled()) { - log.trace("Parameter count: {}", paramList.size()); - for (int i = 0; i < params.length; i++) { - log.trace(" > {}: {}", i, params[i]); - } - } - } - final int dotIndex = action.lastIndexOf('.'); - String serviceName = (dotIndex == -1) ? null : action.substring(0, dotIndex); - String serviceMethod = (dotIndex == -1) ? action : action.substring(dotIndex + 1, action.length()); - log.debug("Service name: {} method: {}", serviceName, serviceMethod); - PendingCall call = new PendingCall(serviceName, serviceMethod, params); - msg.setCall(call); - return msg; - } - - public Notify decodeFlexStreamSend(IoBuffer in) { - // grab the initial limit - int limit = in.limit(); - - // remove the first byte - in.position(1); - in.compact(); - in.rewind(); - - // set the limit back to the original minus the one - // byte that we removed from the buffer - in.limit(limit-1); - - return new FlexStreamSend(in.asReadOnlyBuffer()); - } - - /** - * Checks if the passed action is a reserved stream method. - * - * @param action - * Action to check - * @return true if passed action is a reserved stream method, - * false otherwise - */ - private boolean isStreamCommand(String action) { - switch (StreamAction.getEnum(action)) { - case CREATE_STREAM: - case DELETE_STREAM: - case RELEASE_STREAM: - case PUBLISH: - case PLAY: - case PLAY2: - case SEEK: - case PAUSE: - case PAUSE_RAW: - case CLOSE_STREAM: - case RECEIVE_VIDEO: - case RECEIVE_AUDIO: - return true; - default: - log.debug("Stream action {} is not a recognized command", action); - return false; - } - } - - /** - * Sets incoming connection parameters and / or returns encoded parameters for use in a call. - * - * @param in - * @param notify - * @param input - * @return parameters array - */ - private Object[] handleParameters(IoBuffer in, Notify notify, Input input) { - Object[] params = new Object[] {}; - if (in.hasRemaining()) { - List paramList = new ArrayList(); - final Object obj = Deserializer.deserialize(input, Object.class); - if (obj instanceof Map) { - // Before the actual parameters we sometimes (connect) get a map of parameters, this is usually null, but if set should be - // passed to the connection object. - @SuppressWarnings("unchecked") - final Map connParams = (Map) obj; - notify.setConnectionParams(connParams); - } else if (obj != null) { - paramList.add(obj); - } - while (in.hasRemaining()) { - paramList.add(Deserializer.deserialize(input, Object.class)); - } - params = paramList.toArray(); - if (log.isDebugEnabled()) { - log.debug("Num params: {}", paramList.size()); - for (int i = 0; i < params.length; i++) { - log.debug(" > {}: {}", i, params[i]); - } - } - } - return params; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/codec/RTMPProtocolEncoder.java b/src/main/java/org/red5/server/net/rtmp/codec/RTMPProtocolEncoder.java deleted file mode 100644 index e59ae32d6..000000000 --- a/src/main/java/org/red5/server/net/rtmp/codec/RTMPProtocolEncoder.java +++ /dev/null @@ -1,995 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.codec; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.io.object.Output; -import org.red5.io.object.Serializer; -import org.red5.io.utils.BufferUtils; -import org.red5.server.api.IConnection.Encoding; -import org.red5.server.api.Red5; -import org.red5.server.api.service.IPendingServiceCall; -import org.red5.server.api.service.IServiceCall; -import org.red5.server.api.stream.IClientStream; -import org.red5.server.exception.ClientDetailsException; -import org.red5.server.net.ICommand; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.net.rtmp.RTMPUtils; -import org.red5.server.net.rtmp.codec.RTMP.LiveTimestampMapping; -import org.red5.server.net.rtmp.event.Aggregate; -import org.red5.server.net.rtmp.event.AudioData; -import org.red5.server.net.rtmp.event.BytesRead; -import org.red5.server.net.rtmp.event.ChunkSize; -import org.red5.server.net.rtmp.event.ClientBW; -import org.red5.server.net.rtmp.event.FlexMessage; -import org.red5.server.net.rtmp.event.FlexStreamSend; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.Ping; -import org.red5.server.net.rtmp.event.SWFResponse; -import org.red5.server.net.rtmp.event.ServerBW; -import org.red5.server.net.rtmp.event.SetBuffer; -import org.red5.server.net.rtmp.event.Unknown; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.net.rtmp.event.VideoData.FrameType; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.net.rtmp.message.Packet; -import org.red5.server.net.rtmp.message.SharedObjectTypeMapping; -import org.red5.server.net.rtmp.status.Status; -import org.red5.server.net.rtmp.status.StatusCodes; -import org.red5.server.net.rtmp.status.StatusObject; -import org.red5.server.service.Call; -import org.red5.server.so.ISharedObjectEvent; -import org.red5.server.so.ISharedObjectMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RTMP protocol encoder encodes RTMP messages and packets to byte buffers. - */ -public class RTMPProtocolEncoder implements Constants, IEventEncoder { - - protected static Logger log = LoggerFactory.getLogger(RTMPProtocolEncoder.class); - - /** - * Tolerance (in milliseconds) for late media on streams. A set of levels based on this - * value will be determined. - */ - private long baseTolerance = 15000; - - /** - * Middle tardiness level, between base and this value disposable frames - * will be dropped. Between this and highest value regular interframes will be dropped. - */ - private long midTolerance = baseTolerance + (long) (baseTolerance * 0.3); - - /** - * Highest tardiness level before dropping key frames - */ - private long highestTolerance = baseTolerance + (long) (baseTolerance * 0.6); - - /** - * Indicates if we should drop live packets with future timestamp - * (i.e, when publisher bandwidth is limited) - EXPERIMENTAL - */ - private boolean dropLiveFuture; - - /** - * Encodes object with given protocol state to byte buffer - * - * @param message Object to encode - * @return IoBuffer with encoded data - * @throws Exception Any decoding exception - */ - public IoBuffer encode(Object message) throws Exception { - try { - return encodePacket((Packet) message); - } catch (Exception e) { - log.error("Error encoding", e); - } - return null; - } - - /** - * Encode packet. - * - * @param packet RTMP packet - * @return Encoded data - */ - public IoBuffer encodePacket(Packet packet) { - IoBuffer out = null; - final Header header = packet.getHeader(); - final int channelId = header.getChannelId(); - log.trace("Channel id: {}", channelId); - final IRTMPEvent message = packet.getMessage(); - if (message instanceof ChunkSize) { - ChunkSize chunkSizeMsg = (ChunkSize) message; - ((RTMPConnection) Red5.getConnectionLocal()).getState().setWriteChunkSize(chunkSizeMsg.getSize()); - } - // normally the message is expected not to be dropped - if (!dropMessage(channelId, message)) { - IoBuffer data = encodeMessage(header, message); - if (data != null) { - RTMP rtmp = ((RTMPConnection) Red5.getConnectionLocal()).getState(); - if (data.position() != 0) { - data.flip(); - } else { - data.rewind(); - } - int dataLen = data.limit(); - header.setSize(dataLen); - // get last header - Header lastHeader = rtmp.getLastWriteHeader(channelId); - // maximum header size with extended timestamp (Chunk message header type 0 with 11 byte) - int headerSize = 18; - // set last write header - rtmp.setLastWriteHeader(channelId, header); - // set last write packet - rtmp.setLastWritePacket(channelId, packet); - int chunkSize = rtmp.getWriteChunkSize(); - // maximum chunk header size with extended timestamp - int chunkHeaderSize = 7; - int numChunks = (int) Math.ceil(dataLen / (float) chunkSize); - int bufSize = dataLen + headerSize + (numChunks > 0 ? (numChunks - 1) * chunkHeaderSize : 0); - out = IoBuffer.allocate(bufSize, false); - // encode the header - encodeHeader(header, lastHeader, out); - if (numChunks == 1) { - // we can do it with a single copy - BufferUtils.put(out, data, dataLen); - } else { - int extendedTimestamp = header.getExtendedTimestamp(); - for (int i = 0; i < numChunks - 1; i++) { - BufferUtils.put(out, data, chunkSize); - dataLen -= chunkSize; - RTMPUtils.encodeHeaderByte(out, HEADER_CONTINUE, channelId); - if (extendedTimestamp != 0) { - out.putInt(extendedTimestamp); - } - } - BufferUtils.put(out, data, dataLen); - } - data.free(); - out.flip(); - data = null; - } - } - message.release(); - return out; - } - - /** - * Determine if this message should be dropped for lateness. Live publish data - * does not come through this section, only outgoing data does. - * - * - determine latency between server and client using ping - * - ping timestamp is unsigned int (4 bytes) and is set from value on sender - * - * 1st drop disposable frames - lowest mark - * 2nd drop interframes - middle - * 3rd drop key frames - high mark - * - * @param channelId the channel ID - * @param message the message - * @return true to drop; false to send - */ - protected boolean dropMessage(int channelId, IRTMPEvent message) { - boolean isLive = message.getSourceType() == Constants.SOURCE_TYPE_LIVE; - log.trace("Connection type: {}", (isLive ? "Live" : "VOD")); - if (!isLive) { - return false; - } - //whether or not the packet will be dropped - boolean drop = false; - //whether or not the packet is video data - boolean isVideo = false; - if (message instanceof Ping) { - final Ping pingMessage = (Ping) message; - if (pingMessage.getEventType() == Ping.STREAM_PLAYBUFFER_CLEAR) { - // client buffer cleared, make sure to reset timestamps for this stream - final int channel = (4 + ((pingMessage.getValue2() - 1) * 5)); - ((RTMPConnection) Red5.getConnectionLocal()).getState().clearLastTimestampMapping(channel, channel + 1, channel + 2); - } - // never drop pings - return false; - } - // we only drop audio or video data - if ((isVideo = message instanceof VideoData) || message instanceof AudioData) { - if (message.getTimestamp() == 0) { - // never drop initial packages, also this could be the first packet after - // MP4 seeking and therefore mess with the timestamp mapping - return false; - } - // get connection - RTMPConnection conn = (RTMPConnection) Red5.getConnectionLocal(); - log.trace("Connection: {}", conn); - // get state - RTMP rtmp = conn.getState(); - // determine working type - long timestamp = (message.getTimestamp() & 0xFFFFFFFFL); - LiveTimestampMapping mapping = rtmp.getLastTimestampMapping(channelId); - // just get the current time ONCE per packet - long now = System.currentTimeMillis(); - if (mapping == null || timestamp < mapping.getLastStreamTime()) { - log.trace("Resetting clock time ({}) to stream time ({})", now, timestamp); - // either first time through, or time stamps were reset - mapping = rtmp.new LiveTimestampMapping(now, timestamp); - rtmp.setLastTimestampMapping(channelId, mapping); - } - mapping.setLastStreamTime(timestamp); - long clockTimeOfMessage = mapping.getClockStartTime() + timestamp - mapping.getStreamStartTime(); - //determine tardiness / how late it is - long tardiness = clockTimeOfMessage - now; - //TDJ: EXPERIMENTAL dropping for LIVE packets in future (default false) - if (isLive && dropLiveFuture) { - tardiness = Math.abs(tardiness); - } - //subtract the ping time / latency from the tardiness value - if (conn != null) { - int lastPingTime = conn.getLastPingTime(); - log.trace("Last ping time for connection: {} {} ms", conn.getId(), lastPingTime); - if (lastPingTime > 0) { - tardiness -= lastPingTime; - } - //subtract the buffer time - int streamId = conn.getStreamIdForChannel(channelId); - IClientStream stream = conn.getStreamById(streamId); - if (stream != null) { - int clientBufferDuration = stream.getClientBufferDuration(); - if (clientBufferDuration > 0) { - //two times the buffer duration seems to work best with vod - if (isLive) { - tardiness -= clientBufferDuration; - } else { - tardiness -= clientBufferDuration * 2; - } - } - log.trace("Client buffer duration: {}", clientBufferDuration); - } - } - - //TODO: how should we differ handling based on live or vod? - - //TODO: if we are VOD do we "pause" the provider when we are consistently late? - - if (log.isTraceEnabled()) { - log.trace("Packet timestamp: {}; tardiness: {}; now: {}; message clock time: {}, dropLiveFuture: {}", new Object[] { timestamp, tardiness, now, clockTimeOfMessage, - dropLiveFuture }); - } - //anything coming in less than the base will be allowed to pass, it will not be - //dropped or manipulated - if (tardiness < baseTolerance) { - //frame is below lowest bounds, let it go - } else if (tardiness > highestTolerance) { - //frame is really late, drop it no matter what type - log.trace("Dropping late message: {}", message); - //if we're working with video, indicate that we will need a key frame to proceed - if (isVideo) { - mapping.setKeyFrameNeeded(true); - } - //drop it - drop = true; - } else { - if (isVideo) { - VideoData video = (VideoData) message; - if (video.getFrameType() == FrameType.KEYFRAME) { - //if its a key frame the inter and disposible checks can be skipped - log.trace("Resuming stream with key frame; message: {}", message); - mapping.setKeyFrameNeeded(false); - } else if (tardiness >= baseTolerance && tardiness < midTolerance) { - //drop disposable frames - if (video.getFrameType() == FrameType.DISPOSABLE_INTERFRAME) { - log.trace("Dropping disposible frame; message: {}", message); - drop = true; - } - } else if (tardiness >= midTolerance && tardiness <= highestTolerance) { - //drop inter-frames and disposable frames - log.trace("Dropping disposible or inter frame; message: {}", message); - drop = true; - } - } - } - } - log.trace("Message was{}dropped", (drop ? " " : " not ")); - return drop; - } - - /** - * Determine type of header to use. - * - * @param header RTMP message header - * @param lastHeader Previous header - * @return Header type to use. - */ - private byte getHeaderType(final Header header, final Header lastHeader) { - if (lastHeader == null) { - return HEADER_NEW; - } - final Integer lastFullTs = ((RTMPConnection) Red5.getConnectionLocal()).getState().getLastFullTimestampWritten(header.getChannelId()); - if (lastFullTs == null) { - return HEADER_NEW; - } - final byte headerType; - final long diff = RTMPUtils.diffTimestamps(header.getTimer(), lastHeader.getTimer()); - final long timeSinceFullTs = RTMPUtils.diffTimestamps(header.getTimer(), lastFullTs); - if (header.getStreamId() != lastHeader.getStreamId() || diff < 0 || timeSinceFullTs >= 250) { - // New header mark if header for another stream - headerType = HEADER_NEW; - } else if (header.getSize() != lastHeader.getSize() || header.getDataType() != lastHeader.getDataType()) { - // Same source header if last header data type or size differ - headerType = HEADER_SAME_SOURCE; - } else if (header.getTimer() != lastHeader.getTimer() + lastHeader.getTimerDelta()) { - // Timer change marker if there's time gap between header time stamps - headerType = HEADER_TIMER_CHANGE; - } else { - // Continue encoding - headerType = HEADER_CONTINUE; - } - return headerType; - } - - /** - * Calculate number of bytes necessary to encode the header. - * - * @param header RTMP message header - * @param lastHeader Previous header - * @return Calculated size - */ - private int calculateHeaderSize(final Header header, final Header lastHeader) { - final byte headerType = getHeaderType(header, lastHeader); - int channelIdAdd = 0; - int channelId = header.getChannelId(); - if (channelId > 320) { - channelIdAdd = 2; - } else if (channelId > 63) { - channelIdAdd = 1; - } - return RTMPUtils.getHeaderLength(headerType) + channelIdAdd; - } - - /** - * Encode RTMP header. - * - * @param header RTMP message header - * @param lastHeader Previous header - * @return Encoded header data - */ - public IoBuffer encodeHeader(final Header header, final Header lastHeader) { - final IoBuffer result = IoBuffer.allocate(calculateHeaderSize(header, lastHeader)); - encodeHeader(header, lastHeader, result); - return result; - } - - /** - * Encode RTMP header into given IoBuffer. - * - * @param header RTMP message header - * @param lastHeader Previous header - * @param buf Buffer to write encoded header to - */ - public void encodeHeader(final Header header, final Header lastHeader, final IoBuffer buf) { - final byte headerType = getHeaderType(header, lastHeader); - RTMPUtils.encodeHeaderByte(buf, headerType, header.getChannelId()); - final int timer; - switch (headerType) { - case HEADER_NEW: - timer = header.getTimer(); - if (timer < 0 || timer >= 0xffffff) { - RTMPUtils.writeMediumInt(buf, 0xffffff); - } else { - RTMPUtils.writeMediumInt(buf, timer); - } - RTMPUtils.writeMediumInt(buf, header.getSize()); - buf.put(header.getDataType()); - RTMPUtils.writeReverseInt(buf, header.getStreamId()); - if (timer < 0 || timer >= 0xffffff) { - buf.putInt(timer); - header.setExtendedTimestamp(timer); - } - header.setTimerBase(timer); - header.setTimerDelta(0); - RTMPConnection conn = (RTMPConnection) Red5.getConnectionLocal(); - if (conn != null) { - conn.getState().setLastFullTimestampWritten(header.getChannelId(), timer); - } - break; - case HEADER_SAME_SOURCE: - timer = (int) RTMPUtils.diffTimestamps(header.getTimer(), lastHeader.getTimer()); - if (timer < 0 || timer >= 0xffffff) { - RTMPUtils.writeMediumInt(buf, 0xffffff); - } else { - RTMPUtils.writeMediumInt(buf, timer); - } - RTMPUtils.writeMediumInt(buf, header.getSize()); - buf.put(header.getDataType()); - if (timer < 0 || timer >= 0xffffff) { - buf.putInt(timer); - header.setExtendedTimestamp(timer); - } - header.setTimerBase(header.getTimer() - timer); - header.setTimerDelta(timer); - break; - case HEADER_TIMER_CHANGE: - timer = (int) RTMPUtils.diffTimestamps(header.getTimer(), lastHeader.getTimer()); - if (timer < 0 || timer >= 0xffffff) { - RTMPUtils.writeMediumInt(buf, 0xffffff); - buf.putInt(timer); - header.setExtendedTimestamp(timer); - } else { - RTMPUtils.writeMediumInt(buf, timer); - } - header.setTimerBase(header.getTimer() - timer); - header.setTimerDelta(timer); - break; - case HEADER_CONTINUE: - timer = (int) RTMPUtils.diffTimestamps(header.getTimer(), lastHeader.getTimer()); - header.setTimerBase(header.getTimer() - timer); - header.setTimerDelta(timer); - if (lastHeader.getExtendedTimestamp() != 0) { - buf.putInt(lastHeader.getExtendedTimestamp()); - header.setExtendedTimestamp(lastHeader.getExtendedTimestamp()); - } - break; - default: - break; - } - log.trace("CHUNK, E, {}, {}", header, headerType); - } - - /** - * Encode message. - * - * @param header RTMP message header - * @param message RTMP message (event) - * @return Encoded message data - */ - public IoBuffer encodeMessage(Header header, IRTMPEvent message) { - IServiceCall call = null; - switch (header.getDataType()) { - case TYPE_CHUNK_SIZE: - return encodeChunkSize((ChunkSize) message); - case TYPE_INVOKE: - log.trace("Invoke {}", message); - call = ((Invoke) message).getCall(); - if (call != null) { - log.debug("{}", call.toString()); - Object[] args = call.getArguments(); - if (args != null && args.length > 0) { - Object a0 = args[0]; - if (a0 instanceof Status) { - Status status = (Status) a0; - //code: NetStream.Seek.Notify - if (StatusCodes.NS_SEEK_NOTIFY.equals(status.getCode())) { - //desc: Seeking 25000 (stream ID: 1). - int seekTime = Integer.valueOf(status.getDescription().split(" ")[1]); - log.trace("Seek to time: {}", seekTime); - //audio and video channels - int[] channels = new int[] { 5, 6 }; - //if its a seek notification, reset the "mapping" for audio (5) and video (6) - RTMP rtmp = ((RTMPConnection) Red5.getConnectionLocal()).getState(); - for (int channelId : channels) { - LiveTimestampMapping mapping = rtmp.getLastTimestampMapping(channelId); - if (mapping != null) { - long timestamp = mapping.getClockStartTime() + (seekTime & 0xFFFFFFFFL); - log.trace("Setting last stream time to: {}", timestamp); - mapping.setLastStreamTime(timestamp); - } else { - log.trace("No ts mapping for channel id: {}", channelId); - } - } - } - } - } - } - return encodeInvoke((Invoke) message); - case TYPE_NOTIFY: - log.trace("Notify {}", message); - call = ((Notify) message).getCall(); - if (call == null) { - return encodeStreamMetadata((Notify) message); - } else { - return encodeNotify((Notify) message); - } - case TYPE_PING: - if (message instanceof SetBuffer) { - return encodePing((SetBuffer) message); - } else if (message instanceof SWFResponse) { - return encodePing((SWFResponse) message); - } else { - return encodePing((Ping) message); - } - case TYPE_BYTES_READ: - return encodeBytesRead((BytesRead) message); - case TYPE_AGGREGATE: - log.trace("Encode aggregate message"); - return encodeAggregate((Aggregate) message); - case TYPE_AUDIO_DATA: - log.trace("Encode audio message"); - return encodeAudioData((AudioData) message); - case TYPE_VIDEO_DATA: - log.trace("Encode video message"); - return encodeVideoData((VideoData) message); - case TYPE_FLEX_SHARED_OBJECT: - return encodeFlexSharedObject((ISharedObjectMessage) message); - case TYPE_SHARED_OBJECT: - return encodeSharedObject((ISharedObjectMessage) message); - case TYPE_SERVER_BANDWIDTH: - return encodeServerBW((ServerBW) message); - case TYPE_CLIENT_BANDWIDTH: - return encodeClientBW((ClientBW) message); - case TYPE_FLEX_MESSAGE: - return encodeFlexMessage((FlexMessage) message); - case TYPE_FLEX_STREAM_SEND: - return encodeFlexStreamSend((FlexStreamSend) message); - default: - log.warn("Unknown object type: {}", header.getDataType()); - } - return null; - } - - /** - * Encode server-side bandwidth event. - * - * @param serverBW Server-side bandwidth event - * @return Encoded event data - */ - private IoBuffer encodeServerBW(ServerBW serverBW) { - final IoBuffer out = IoBuffer.allocate(4); - out.putInt(serverBW.getBandwidth()); - return out; - } - - /** - * Encode client-side bandwidth event. - * - * @param clientBW Client-side bandwidth event - * @return Encoded event data - */ - private IoBuffer encodeClientBW(ClientBW clientBW) { - final IoBuffer out = IoBuffer.allocate(5); - out.putInt(clientBW.getBandwidth()); - out.put(clientBW.getLimitType()); - return out; - } - - /** {@inheritDoc} */ - public IoBuffer encodeChunkSize(ChunkSize chunkSize) { - final IoBuffer out = IoBuffer.allocate(4); - out.putInt(chunkSize.getSize()); - return out; - } - - /** {@inheritDoc} */ - public IoBuffer encodeFlexSharedObject(ISharedObjectMessage so) { - final IoBuffer out = IoBuffer.allocate(128); - out.setAutoExpand(true); - out.put((byte) 0x00); // unknown (not AMF version) - doEncodeSharedObject(so, out); - return out; - } - - /** {@inheritDoc} */ - public IoBuffer encodeSharedObject(ISharedObjectMessage so) { - final IoBuffer out = IoBuffer.allocate(128); - out.setAutoExpand(true); - doEncodeSharedObject(so, out); - return out; - } - - /** - * Perform the actual encoding of the shared object contents. - * - * @param so shared object - * @param out output buffer - */ - private void doEncodeSharedObject(ISharedObjectMessage so, IoBuffer out) { - final Encoding encoding = Red5.getConnectionLocal().getEncoding(); - final Output output = new org.red5.io.amf.Output(out); - final Output amf3output = new org.red5.io.amf3.Output(out); - output.putString(so.getName()); - // SO version - out.putInt(so.getVersion()); - // Encoding (this always seems to be 2 for persistent shared objects) - out.putInt(so.isPersistent() ? 2 : 0); - // unknown field - out.putInt(0); - int mark, len; - for (final ISharedObjectEvent event : so.getEvents()) { - final ISharedObjectEvent.Type eventType = event.getType(); - byte type = SharedObjectTypeMapping.toByte(eventType); - switch (eventType) { - case SERVER_CONNECT: - case CLIENT_INITIAL_DATA: - case CLIENT_CLEAR_DATA: - out.put(type); - out.putInt(0); - break; - case SERVER_DELETE_ATTRIBUTE: - case CLIENT_DELETE_DATA: - case CLIENT_UPDATE_ATTRIBUTE: - out.put(type); - mark = out.position(); - out.skip(4); // we will be back - output.putString(event.getKey()); - len = out.position() - mark - 4; - out.putInt(mark, len); - break; - case SERVER_SET_ATTRIBUTE: - case CLIENT_UPDATE_DATA: - if (event.getKey() == null) { - // Update multiple attributes in one request - Map initialData = (Map) event.getValue(); - for (Object o : initialData.keySet()) { - out.put(type); - mark = out.position(); - out.skip(4); // we will be back - String key = (String) o; - output.putString(key); - if (encoding == Encoding.AMF3) { - Serializer.serialize(amf3output, initialData.get(key)); - } else { - Serializer.serialize(output, initialData.get(key)); - } - len = out.position() - mark - 4; - out.putInt(mark, len); - } - } else { - out.put(type); - mark = out.position(); - out.skip(4); // we will be back - output.putString(event.getKey()); - if (encoding == Encoding.AMF3) { - Serializer.serialize(amf3output, event.getValue()); - } else { - Serializer.serialize(output, event.getValue()); - } - len = out.position() - mark - 4; - out.putInt(mark, len); - } - break; - case CLIENT_SEND_MESSAGE: - case SERVER_SEND_MESSAGE: - // Send method name and value - out.put(type); - mark = out.position(); - out.skip(4); - // Serialize name of the handler to call... - Serializer.serialize(output, event.getKey()); - try { - List arguments = (List) event.getValue(); - if (arguments != null) { - // ...and the arguments - for (Object arg : arguments) { - if (encoding == Encoding.AMF3) { - Serializer.serialize(amf3output, arg); - } else { - Serializer.serialize(output, arg); - } - } - } else { - // serialize a null as the arguments - if (encoding == Encoding.AMF3) { - Serializer.serialize(amf3output, null); - } else { - Serializer.serialize(output, null); - } - } - } catch (Exception ex) { - log.warn("Exception encoding args for event: {}", event, ex); - } - len = out.position() - mark - 4; - //log.debug(len); - out.putInt(mark, len); - //log.info(out.getHexDump()); - break; - case CLIENT_STATUS: - out.put(type); - final String status = event.getKey(); - final String message = (String) event.getValue(); - out.putInt(message.length() + status.length() + 4); - output.putString(message); - output.putString(status); - break; - default: - log.warn("Unknown event: {}", eventType); - // XXX: need to make this work in server or client mode - out.put(type); - mark = out.position(); - out.skip(4); // we will be back - output.putString(event.getKey()); - if (encoding == Encoding.AMF3) { - Serializer.serialize(amf3output, event.getValue()); - } else { - Serializer.serialize(output, event.getValue()); - } - len = out.position() - mark - 4; - out.putInt(mark, len); - break; - } - } - } - - /** {@inheritDoc} */ - public IoBuffer encodeNotify(Notify notify) { - return encodeCommand(notify); - } - - /** {@inheritDoc} */ - public IoBuffer encodeInvoke(Invoke invoke) { - return encodeCommand(invoke); - } - - /** - * Encode notification event. - * - * @param invoke Notification event - * @return Encoded event data - */ - protected IoBuffer encodeCommand(Notify invoke) { - IoBuffer out = IoBuffer.allocate(1024); - out.setAutoExpand(true); - encodeCommand(out, invoke); - return out; - } - - /** - * Encode command event and fill given byte buffer. - * - * @param out Buffer to fill - * @param command Command event - */ - protected void encodeCommand(IoBuffer out, ICommand command) { - // TODO: tidy up here - Output output = new org.red5.io.amf.Output(out); - final IServiceCall call = command.getCall(); - final boolean isPending = (call.getStatus() == Call.STATUS_PENDING); - log.debug("Call: {} pending: {}", call, isPending); - if (!isPending) { - log.debug("Call has been executed, send result"); - Serializer.serialize(output, call.isSuccess() ? "_result" : "_error"); - } else { - log.debug("This is a pending call, send request"); - final String action = (call.getServiceName() == null) ? call.getServiceMethodName() : call.getServiceName() + '.' + call.getServiceMethodName(); - Serializer.serialize(output, action); // seems right - } - if (command instanceof Invoke) { - Serializer.serialize(output, Integer.valueOf(command.getTransactionId())); - Serializer.serialize(output, command.getConnectionParams()); - } - if (call.getServiceName() == null && "connect".equals(call.getServiceMethodName())) { - // response to initial connect, always use AMF0 - output = new org.red5.io.amf.Output(out); - } else { - if (Red5.getConnectionLocal().getEncoding() == Encoding.AMF3) { - output = new org.red5.io.amf3.Output(out); - } else { - output = new org.red5.io.amf.Output(out); - } - } - if (!isPending && (command instanceof Invoke)) { - IPendingServiceCall pendingCall = (IPendingServiceCall) call; - if (!call.isSuccess()) { - log.debug("Call was not successful"); - StatusObject status = generateErrorResult(StatusCodes.NC_CALL_FAILED, call.getException()); - pendingCall.setResult(status); - } - Object res = pendingCall.getResult(); - log.debug("Writing result: {}", res); - Serializer.serialize(output, res); - } else { - log.debug("Writing params"); - final Object[] args = call.getArguments(); - if (args != null) { - for (Object element : args) { - if (element instanceof ByteBuffer) { - // a byte buffer indicates that serialization is already complete, send raw - final ByteBuffer buf = (ByteBuffer) element; - buf.mark(); - try { - out.put(buf); - } finally { - buf.reset(); - } - } else { - // standard serialize - Serializer.serialize(output, element); - } - } - } - } - if (command.getData() != null) { - out.setAutoExpand(true); - out.put(command.getData()); - } - } - - /** {@inheritDoc} */ - public IoBuffer encodePing(Ping ping) { - int len; - short type = ping.getEventType(); - switch (type) { - case Ping.CLIENT_BUFFER: - len = 10; - break; - case Ping.PONG_SWF_VERIFY: - len = 44; - break; - default: - len = 6; - } - final IoBuffer out = IoBuffer.allocate(len); - out.putShort(type); - switch (type) { - case Ping.STREAM_BEGIN: - case Ping.STREAM_PLAYBUFFER_CLEAR: - case Ping.STREAM_DRY: - case Ping.RECORDED_STREAM: - case Ping.PING_CLIENT: - case Ping.PONG_SERVER: - case Ping.BUFFER_EMPTY: - case Ping.BUFFER_FULL: - out.putInt(ping.getValue2()); - break; - case Ping.CLIENT_BUFFER: - if (ping instanceof SetBuffer) { - SetBuffer setBuffer = (SetBuffer) ping; - out.putInt(setBuffer.getStreamId()); - out.putInt(setBuffer.getBufferLength()); - } else { - out.putInt(ping.getValue2()); - out.putInt(ping.getValue3()); - } - break; - case Ping.PING_SWF_VERIFY: - break; - case Ping.PONG_SWF_VERIFY: - out.put(((SWFResponse) ping).getBytes()); - break; - } - // this may not be needed anymore - if (ping.getValue4() != Ping.UNDEFINED) { - out.putInt(ping.getValue4()); - } - return out; - } - - /** {@inheritDoc} */ - public IoBuffer encodeBytesRead(BytesRead bytesRead) { - final IoBuffer out = IoBuffer.allocate(4); - out.putInt(bytesRead.getBytesRead()); - return out; - } - - /** {@inheritDoc} */ - public IoBuffer encodeAggregate(Aggregate aggregate) { - final IoBuffer result = aggregate.getData(); - return result; - } - - /** {@inheritDoc} */ - public IoBuffer encodeAudioData(AudioData audioData) { - final IoBuffer result = audioData.getData(); - return result; - } - - /** {@inheritDoc} */ - public IoBuffer encodeVideoData(VideoData videoData) { - final IoBuffer result = videoData.getData(); - return result; - } - - /** {@inheritDoc} */ - public IoBuffer encodeUnknown(Unknown unknown) { - final IoBuffer result = unknown.getData(); - return result; - } - - public IoBuffer encodeStreamMetadata(Notify metaData) { - final IoBuffer result = metaData.getData(); - return result; - } - - /** - * Generate error object to return for given exception. - * - * @param code call - * @param error error - * @return status object - */ - protected StatusObject generateErrorResult(String code, Throwable error) { - // Construct error object to return - String message = ""; - while (error != null && error.getCause() != null) { - error = error.getCause(); - } - if (error != null && error.getMessage() != null) { - message = error.getMessage(); - } - StatusObject status = new StatusObject(code, "error", message); - if (error instanceof ClientDetailsException) { - // Return exception details to client - status.setApplication(((ClientDetailsException) error).getParameters()); - if (((ClientDetailsException) error).includeStacktrace()) { - List stack = new ArrayList(); - for (StackTraceElement element : error.getStackTrace()) { - stack.add(element.toString()); - } - status.setAdditional("stacktrace", stack); - } - } else if (error != null) { - status.setApplication(error.getClass().getCanonicalName()); - List stack = new ArrayList(); - for (StackTraceElement element : error.getStackTrace()) { - stack.add(element.toString()); - } - status.setAdditional("stacktrace", stack); - } - return status; - } - - /** - * Encodes Flex message event. - * - * @param msg Flex message event - * @return Encoded data - */ - public IoBuffer encodeFlexMessage(FlexMessage msg) { - IoBuffer out = IoBuffer.allocate(1024); - out.setAutoExpand(true); - // Unknown byte, always 0? - out.put((byte) 0); - encodeCommand(out, msg); - return out; - } - - public IoBuffer encodeFlexStreamSend(FlexStreamSend msg) { - final IoBuffer result = msg.getData(); - return result; - } - - private void updateTolerance() { - midTolerance = baseTolerance + (long) (baseTolerance * 0.3); - highestTolerance = baseTolerance + (long) (baseTolerance * 0.6); - } - - public void setBaseTolerance(long baseTolerance) { - this.baseTolerance = baseTolerance; - //update high and low tolerance - updateTolerance(); - } - - /** - * Setter for dropLiveFuture - */ - public void setDropLiveFuture(boolean dropLiveFuture) { - this.dropLiveFuture = dropLiveFuture; - } - - public long getBaseTolerance() { - return baseTolerance; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/Abort.java b/src/main/java/org/red5/server/net/rtmp/event/Abort.java deleted file mode 100644 index ce6b6274c..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/Abort.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -/** - * RTMP Abort event. - * - * @author aclarke@xuggle.com - */ -public class Abort extends BaseEvent { - - private int channelId = 0; - - public Abort() { - super(Type.SYSTEM); - } - - public Abort(int channelId) { - this.channelId = channelId; - } - - public byte getDataType() { - return TYPE_ABORT; - } - - protected void releaseInternal() { - - } - - public void setChannelId(int channelId) { - this.channelId = channelId; - } - - public int getChannelId() { - return channelId; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "Abort Channel: " + channelId; - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - channelId = in.readInt(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeInt(channelId); - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/net/rtmp/event/Aggregate.java b/src/main/java/org/red5/server/net/rtmp/event/Aggregate.java deleted file mode 100644 index a0f4d9b19..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/Aggregate.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectInputStream; -import java.io.ObjectOutput; -import java.io.ObjectOutputStream; -import java.util.LinkedList; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.io.IoConstants; -import org.red5.io.utils.IOUtils; -import org.red5.server.api.stream.IStreamPacket; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.stream.IStreamData; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Aggregate data event - */ -public class Aggregate extends BaseEvent implements IoConstants, IStreamData, IStreamPacket { - - private static final long serialVersionUID = 5538859593815804830L; - - private static Logger log = LoggerFactory.getLogger(Aggregate.class); - - /** - * Data - */ - protected IoBuffer data; - - /** - * Data type - */ - private byte dataType = TYPE_AGGREGATE; - - /** Constructs a new Aggregate. */ - public Aggregate() { - this(IoBuffer.allocate(0).flip()); - } - - /** - * Create aggregate data event with given data buffer - * @param data - */ - public Aggregate(IoBuffer data) { - super(Type.STREAM_DATA); - setData(data); - } - - /** - * Create aggregate data event with given data buffer - * @param data aggregate data - * @param copy true to use a copy of the data or false to use reference - */ - public Aggregate(IoBuffer data, boolean copy) { - super(Type.STREAM_DATA); - if (copy) { - byte[] array = new byte[data.limit()]; - data.mark(); - data.get(array); - data.reset(); - setData(array); - } else { - setData(data); - } - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return dataType; - } - - public void setDataType(byte dataType) { - this.dataType = dataType; - } - - /** {@inheritDoc} */ - public IoBuffer getData() { - return data; - } - - public void setData(IoBuffer data) { - this.data = data; - } - - public void setData(byte[] data) { - this.data = IoBuffer.allocate(data.length); - this.data.put(data).flip(); - } - - /** - * Breaks-up the aggregate into its individual parts and returns them as a list. - * The parts are returned based on the ordering of the aggregate itself. - * - * @return list of IRTMPEvent objects - */ - public LinkedList getParts() { - LinkedList parts = new LinkedList(); - log.trace("Aggregate data length: {}", data.limit()); - int position = data.position(); - do { - try { - // read the header - //log.trace("Hex: {}", data.getHexDump()); - byte subType = data.get(); - // when we run into subtype 0 break out of here - if (subType == 0) { - log.debug("Subtype 0 encountered within this aggregate, processing with exit"); - break; - } - int size = IOUtils.readUnsignedMediumInt(data); - log.debug("Data subtype: {} size: {}", subType, size); - // TODO ensure the data contains all the bytes to support the specified size - int timestamp = IOUtils.readExtendedMediumInt(data); - /*timestamp = ntohap((GETIBPOINTER(buffer) + 4)); 0x12345678 == 34 56 78 12*/ - int streamId = IOUtils.readUnsignedMediumInt(data); - log.debug("Data timestamp: {} stream id: {}", timestamp, streamId); - Header partHeader = new Header(); - partHeader.setChannelId(header.getChannelId()); - partHeader.setDataType(subType); - partHeader.setSize(size); - // use the stream id from the aggregate's header - partHeader.setStreamId(header.getStreamId()); - partHeader.setTimer(timestamp); - // timer delta == time stamp - timer base - // the back pointer may be used to verify the size of the individual part - // it will be equal to the data size + header size - int backPointer = 0; - switch (subType) { - case TYPE_AUDIO_DATA: - AudioData audio = new AudioData(data.getSlice(size)); - audio.setTimestamp(timestamp); - audio.setHeader(partHeader); - log.debug("Audio header: {}", audio.getHeader()); - parts.add(audio); - //log.trace("Hex: {}", data.getHexDump()); - // ensure 4 bytes left to read an int - if (data.position() < data.limit() - 4) { - backPointer = data.getInt(); - //log.trace("Back pointer: {}", backPointer); - if (backPointer != (size + 11)) { - log.debug("Data size ({}) and back pointer ({}) did not match", size, backPointer); - } - } - break; - case TYPE_VIDEO_DATA: - VideoData video = new VideoData(data.getSlice(size)); - video.setTimestamp(timestamp); - video.setHeader(partHeader); - log.debug("Video header: {}", video.getHeader()); - parts.add(video); - //log.trace("Hex: {}", data.getHexDump()); - // ensure 4 bytes left to read an int - if (data.position() < data.limit() - 4) { - backPointer = data.getInt(); - //log.trace("Back pointer: {}", backPointer); - if (backPointer != (size + 11)) { - log.debug("Data size ({}) and back pointer ({}) did not match", size, backPointer); - } - } - break; - default: - log.debug("Non-A/V subtype: {}", subType); - Unknown unk = new Unknown(subType, data.getSlice(size)); - unk.setTimestamp(timestamp); - unk.setHeader(partHeader); - parts.add(unk); - // ensure 4 bytes left to read an int - if (data.position() < data.limit() - 4) { - backPointer = data.getInt(); - } - } - position = data.position(); - } catch (Exception e) { - log.error("Exception decoding aggregate parts", e); - break; - } - log.trace("Data position: {}", position); - } while (position < data.limit()); - log.trace("Aggregate processing complete, {} parts extracted", parts.size()); - return parts; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return String.format("Aggregate - ts: %s length: %s", getTimestamp(), (data != null ? data.limit() : '0')); - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - if (data != null) { - final IoBuffer localData = data; - // null out the data first so we don't accidentally - // return a valid reference first - data = null; - localData.clear(); - localData.free(); - } - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - byte[] byteBuf = (byte[]) in.readObject(); - if (byteBuf != null) { - data = IoBuffer.allocate(byteBuf.length); - data.setAutoExpand(true); - SerializeUtils.ByteArrayToByteBuffer(byteBuf, data); - } - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - if (data != null) { - out.writeObject(SerializeUtils.ByteBufferToByteArray(data)); - } else { - out.writeObject(null); - } - } - - /** - * Duplicate this message / event. - * - * @return duplicated event - */ - public Aggregate duplicate() throws IOException, ClassNotFoundException { - Aggregate result = new Aggregate(); - // serialize - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - writeExternal(oos); - oos.close(); - // convert to byte array - byte[] buf = baos.toByteArray(); - baos.close(); - // create input streams - ByteArrayInputStream bais = new ByteArrayInputStream(buf); - ObjectInputStream ois = new ObjectInputStream(bais); - // deserialize - result.readExternal(ois); - ois.close(); - bais.close(); - // clone the header if there is one - if (header != null) { - result.setHeader(header.clone()); - } - return result; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/AllocationDebugger.java b/src/main/java/org/red5/server/net/rtmp/event/AllocationDebugger.java deleted file mode 100644 index 5f1ad72c5..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/AllocationDebugger.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - - -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Simple allocation debugger for Event reference counting. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) on behalf of - * (ce@publishing-etc.de) - * @author Paul Gregoire (mondain@gmail.com) - */ -public class AllocationDebugger { - - /** - * Information on references count - */ - private static class Info { - /** - * References count - */ - public AtomicInteger refcount = new AtomicInteger(1); - - /** Constructs a new Info. */ - public Info() { - } - - } - - /** - * Allocation debugger istance - */ - private static AllocationDebugger instance = new AllocationDebugger(); - - /** - * Logger - */ - private Logger log; - - /** - * Events-to-information objects map - */ - private ConcurrentMap events; - - /** - * Getter for instance - * - * @return Allocation debugger instance - */ - public static AllocationDebugger getInstance() { - return instance; - } - - /** Do not instantiate AllocationDebugger. */ - private AllocationDebugger() { - log = LoggerFactory.getLogger(getClass()); - events = new ConcurrentHashMap(); - } - - /** - * Add event to map - * - * @param event - * Event - */ - protected void create(BaseEvent event) { - events.put(event, new Info()); - } - - /** - * Retain event - * - * @param event - * Event - */ - protected void retain(BaseEvent event) { - Info info = events.get(event); - if (info != null) { - info.refcount.incrementAndGet(); - } else { - log.warn("Retain called on already released event."); - } - } - - /** - * Release event if there's no more references to it - * - * @param event - * Event - */ - protected void release(BaseEvent event) { - Info info = events.get(event); - if (info != null) { - if (info.refcount.decrementAndGet() == 0) { - events.remove(event); - } - } else { - log.warn("Release called on already released event."); - } - } - - /** - * Dumps allocations - */ - public synchronized void dump() { - if (log.isDebugEnabled()) { - log.debug("dumping allocations {}", events.size()); - for (Entry entry : events.entrySet()) { - log.debug("{} {}", entry.getKey(), entry.getValue().refcount); - } - } - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/AudioData.java b/src/main/java/org/red5/server/net/rtmp/event/AudioData.java deleted file mode 100644 index 71278217c..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/AudioData.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectInputStream; -import java.io.ObjectOutput; -import java.io.ObjectOutputStream; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.stream.IStreamPacket; -import org.red5.server.stream.IStreamData; - -public class AudioData extends BaseEvent implements IStreamData, IStreamPacket { - - private static final long serialVersionUID = -4102940670913999407L; - - protected IoBuffer data; - - /** - * Data type - */ - private byte dataType = TYPE_AUDIO_DATA; - - /** Constructs a new AudioData. */ - public AudioData() { - this(IoBuffer.allocate(0).flip()); - } - - public AudioData(IoBuffer data) { - super(Type.STREAM_DATA); - setData(data); - } - - /** - * Create audio data event with given data buffer - * @param data Audio data - * @param copy true to use a copy of the data or false to use reference - */ - public AudioData(IoBuffer data, boolean copy) { - super(Type.STREAM_DATA); - if (copy) { - byte[] array = new byte[data.limit()]; - data.mark(); - data.get(array); - data.reset(); - setData(array); - } else { - setData(data); - } - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return dataType; - } - - public void setDataType(byte dataType) { - this.dataType = dataType; - } - - /** {@inheritDoc} */ - public IoBuffer getData() { - return data; - } - - public void setData(IoBuffer data) { - this.data = data; - } - - public void setData(byte[] data) { - this.data = IoBuffer.allocate(data.length); - this.data.put(data).flip(); - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - if (data != null) { - data.free(); - data = null; - } - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - byte[] byteBuf = (byte[]) in.readObject(); - if (byteBuf != null) { - data = IoBuffer.allocate(0); - data.setAutoExpand(true); - SerializeUtils.ByteArrayToByteBuffer(byteBuf, data); - } - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - if (data != null) { - out.writeObject(SerializeUtils.ByteBufferToByteArray(data)); - } else { - out.writeObject(null); - } - } - - /** - * Duplicate this message / event. - * - * @return duplicated event - */ - public AudioData duplicate() throws IOException, ClassNotFoundException { - AudioData result = new AudioData(); - // serialize - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - writeExternal(oos); - oos.close(); - // convert to byte array - byte[] buf = baos.toByteArray(); - baos.close(); - // create input streams - ByteArrayInputStream bais = new ByteArrayInputStream(buf); - ObjectInputStream ois = new ObjectInputStream(bais); - // deserialize - result.readExternal(ois); - ois.close(); - bais.close(); - // clone the header if there is one - if (header != null) { - result.setHeader(header.clone()); - } - return result; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return String.format("Audio - ts: %s length: %s", getTimestamp(), (data != null ? data.limit() : '0')); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/BaseEvent.java b/src/main/java/org/red5/server/net/rtmp/event/BaseEvent.java deleted file mode 100644 index 2fe2695db..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/BaseEvent.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; -import java.util.concurrent.atomic.AtomicInteger; - -import org.red5.server.api.event.IEventListener; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.net.rtmp.message.Header; - -/** - * Base abstract class for all RTMP events - */ -public abstract class BaseEvent implements Constants, IRTMPEvent, Externalizable { - - // XXX we need a better way to inject allocation debugging - // (1) make it configurable in xml - // (2) make it aspect oriented - private static final boolean allocationDebugging = false; - - /** - * Event type - */ - private Type type; - - /** - * Source type - */ - private byte sourceType; - - /** - * Event target object - */ - protected Object object; - - /** - * Event listener - */ - protected IEventListener source; - - /** - * Event timestamp - */ - protected int timestamp; - - /** - * Event RTMP packet header - */ - protected Header header = null; - - /** - * Event references count - */ - protected AtomicInteger refcount = new AtomicInteger(1); - - public BaseEvent() { - // set a default type - this(Type.SERVER, null); - } - - /** - * Create new event of given type - * @param type Event type - */ - public BaseEvent(Type type) { - this(type, null); - } - - /** - * Create new event of given type - * @param type Event type - * @param source Event source - */ - public BaseEvent(Type type, IEventListener source) { - this.type = type; - this.source = source; - if (allocationDebugging) { - AllocationDebugger.getInstance().create(this); - } - } - - /** {@inheritDoc} */ - public Type getType() { - return type; - } - - public void setType(Type type) { - this.type = type; - } - - public byte getSourceType() { - return sourceType; - } - - public void setSourceType(byte sourceType) { - this.sourceType = sourceType; - } - - /** {@inheritDoc} */ - public Object getObject() { - return object; - } - - /** {@inheritDoc} */ - public Header getHeader() { - return header; - } - - /** {@inheritDoc} */ - public void setHeader(Header header) { - this.header = header; - } - - /** {@inheritDoc} */ - public boolean hasSource() { - return source != null; - } - - /** {@inheritDoc} */ - public IEventListener getSource() { - return source; - } - - /** {@inheritDoc} */ - public void setSource(IEventListener source) { - this.source = source; - } - - /** {@inheritDoc} */ - public abstract byte getDataType(); - - /** {@inheritDoc} */ - public int getTimestamp() { - return timestamp; - } - - /** {@inheritDoc} */ - public void setTimestamp(int timestamp) { - this.timestamp = timestamp; - } - - /** {@inheritDoc} */ - @SuppressWarnings("all") - public void retain() { - if (allocationDebugging) { - AllocationDebugger.getInstance().retain(this); - } - final int baseCount = refcount.getAndIncrement(); - if (allocationDebugging && baseCount < 1) { - throw new RuntimeException("attempt to retain object with invalid ref count"); - } - } - - /** {@inheritDoc} */ - @SuppressWarnings("all") - public void release() { - if (allocationDebugging) { - AllocationDebugger.getInstance().release(this); - } - final int baseCount = refcount.decrementAndGet(); - if (baseCount == 0) { - releaseInternal(); - } else if (allocationDebugging && baseCount < 0) { - throw new RuntimeException("attempt to retain object with invalid ref count"); - } - } - - /** - * Release event - */ - protected abstract void releaseInternal(); - - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - type = (Type) in.readObject(); - sourceType = in.readByte(); - timestamp = in.readInt(); - } - - public void writeExternal(ObjectOutput out) throws IOException { - out.writeObject(type); - out.writeByte(sourceType); - out.writeInt(timestamp); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/BytesRead.java b/src/main/java/org/red5/server/net/rtmp/event/BytesRead.java deleted file mode 100644 index f8076da16..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/BytesRead.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -/** - * Bytes read event - */ -public class BytesRead extends BaseEvent { - - private static final long serialVersionUID = -127649312402709338L; - - /** - * Bytes read - */ - private int bytesRead; - - public BytesRead() { - super(Type.STREAM_CONTROL); - } - - /** - * Creates new event with given bytes number - * @param bytesRead Number of bytes read - */ - public BytesRead(int bytesRead) { - this(); - this.bytesRead = bytesRead; - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return TYPE_BYTES_READ; - } - - /** - * Return number of bytes read - * - * @return Number of bytes - */ - public int getBytesRead() { - return bytesRead; - } - - /** - * Setter for bytes read - * - * @param bytesRead Number of bytes read - */ - public void setBytesRead(int bytesRead) { - this.bytesRead = bytesRead; - } - - /** - * Release event (set bytes read to zero) - */ - protected void doRelease() { - bytesRead = 0; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "StreamBytesRead: " + bytesRead; - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - bytesRead = in.readInt(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeInt(bytesRead); - } -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/net/rtmp/event/CachedEvent.java b/src/main/java/org/red5/server/net/rtmp/event/CachedEvent.java deleted file mode 100644 index a497b0dd8..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/CachedEvent.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.stream.IStreamPacket; - -/** - * Provides a means for storage of RTMP events. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class CachedEvent implements IStreamPacket { - - /** - * Event timestamp - */ - private int timestamp; - - /** - * Time at which the event entered the server - */ - private long receivedTime; - - private byte dataType; - - private IoBuffer data; - - /** - * @return the timestamp - */ - public int getTimestamp() { - return timestamp; - } - - /** - * @param timestamp the timestamp to set - */ - public void setTimestamp(int timestamp) { - this.timestamp = timestamp; - } - - /** - * @return the receivedTime - */ - public long getReceivedTime() { - return receivedTime; - } - - /** - * @param receivedTime the receivedTime to set - */ - public void setReceivedTime(long receivedTime) { - this.receivedTime = receivedTime; - } - - /** - * @return the dataType - */ - public byte getDataType() { - return dataType; - } - - /** - * @param dataType the dataType to set - */ - public void setDataType(byte dataType) { - this.dataType = dataType; - } - - /** - * @return the data - */ - public IoBuffer getData() { - return data; - } - - /** - * @param data the data to set - */ - public void setData(IoBuffer data) { - this.data = data; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + dataType; - result = prime * result + timestamp; - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - CachedEvent other = (CachedEvent) obj; - if (dataType != other.dataType) - return false; - if (timestamp != other.timestamp) - return false; - return true; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/ChunkSize.java b/src/main/java/org/red5/server/net/rtmp/event/ChunkSize.java deleted file mode 100644 index c0bbee665..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/ChunkSize.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -/** - * Chunk size event - */ -public class ChunkSize extends BaseEvent { - - private static final long serialVersionUID = -7680099175881755879L; - - /** - * Chunk size - */ - private int size; - - public ChunkSize() { - super(Type.SYSTEM); - } - /** - * Create chunk size event with given size - * @param size Chunk size - */ - public ChunkSize(int size) { - this(); - this.size = size; - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return TYPE_CHUNK_SIZE; - } - - /** - * Getter for size. - * - * @return Chunk size - */ - public int getSize() { - return size; - } - - /** - * Setter for size. - * - * @param size Chunk size - */ - public void setSize(int size) { - this.size = size; - } - - /** - * Releases chunk (set size to zero) - */ - protected void doRelease() { - size = 0; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "ChunkSize: " + size; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ChunkSize)) { - return false; - } - final ChunkSize other = (ChunkSize) obj; - return getSize() == other.getSize(); - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - // XXX Paul: use timestamp as the hash instead of Object.hashCode() - return timestamp; - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - size = in.readInt(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeInt(size); - } -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/net/rtmp/event/ClientBW.java b/src/main/java/org/red5/server/net/rtmp/event/ClientBW.java deleted file mode 100644 index 33a0862e7..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/ClientBW.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -/** - * Client bandwidth event. Also known as a Peer Bandwidth message. - */ -public class ClientBW extends BaseEvent { - - private static final long serialVersionUID = 5848656135751336839L; - - /** - * Bandwidth - */ - private int bandwidth; - - /** - * Enforcement level or limit type of the bandwidth value based on three values. - *
-	 * TYPE_HARD 0
-	 * TYPE_SOFT 1
-	 * TYPE_DYNAMIC 2
-	 * 
- */ - private byte limitType; - - public ClientBW() { - super(Type.STREAM_CONTROL); - } - - public ClientBW(int bandwidth, byte limitType) { - this(); - this.bandwidth = bandwidth; - this.limitType = limitType; - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return TYPE_CLIENT_BANDWIDTH; - } - - /** - * Getter for property 'bandwidth'. - * - * @return Value for property 'bandwidth'. - */ - public int getBandwidth() { - return bandwidth; - } - - /** - * Setter for bandwidth - * - * @param bandwidth New bandwidth - */ - public void setBandwidth(int bandwidth) { - this.bandwidth = bandwidth; - } - - /** - * Getter for limitType - * - * @return limitType for property 'limitType'. - */ - public byte getLimitType() { - return limitType; - } - - /** - * Setter for property 'limitType'. - * - * @param limitType Value to set for property 'limitType'. - */ - public void setLimitType(byte limitType) { - this.limitType = limitType; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "ClientBW: " + bandwidth + " limitType: " + limitType; - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - bandwidth = in.readInt(); - limitType = in.readByte(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeInt(bandwidth); - out.writeByte(limitType); - } -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/ClientInvokeEvent.java b/src/main/java/org/red5/server/net/rtmp/event/ClientInvokeEvent.java deleted file mode 100644 index c52907b13..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/ClientInvokeEvent.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.red5.server.net.rtmp.event; - -import java.util.Arrays; - -import org.red5.server.api.service.IPendingServiceCallback; - -/** - * Represents an invoke to be executed on a connected client. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class ClientInvokeEvent extends BaseEvent { - - private final String method; - private final Object[] params; - private final IPendingServiceCallback callback; - - public ClientInvokeEvent(String method, Object[] params, IPendingServiceCallback callback) { - super(Type.CLIENT_INVOKE); - this.method = method; - this.params = params; - this.callback = callback; - } - - public final static ClientInvokeEvent build(String method, Object[] params, IPendingServiceCallback callback) { - ClientInvokeEvent event = new ClientInvokeEvent(method, params, callback); - return event; - } - - @Override - public byte getDataType() { - return TYPE_INVOKE; - } - - @Override - protected void releaseInternal() { - } - - /** - * @return the method - */ - public String getMethod() { - return method; - } - - /** - * @return the params - */ - public Object[] getParams() { - return params; - } - - /** - * @return the callback - */ - public IPendingServiceCallback getCallback() { - return callback; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "ClientInvokeEvent [method=" + method + ", params=" + Arrays.toString(params) + ", callback=" + callback + "]"; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/ClientNotifyEvent.java b/src/main/java/org/red5/server/net/rtmp/event/ClientNotifyEvent.java deleted file mode 100644 index 25ec84b61..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/ClientNotifyEvent.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.red5.server.net.rtmp.event; - -import java.util.Arrays; - -/** - * Represents an notify to be executed on a connected client. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class ClientNotifyEvent extends BaseEvent { - - private final String method; - private final Object[] params; - - public ClientNotifyEvent(String method, Object[] params) { - super(Type.CLIENT_NOTIFY); - this.method = method; - this.params = params; - } - - public final static ClientNotifyEvent build(String method, Object[] params) { - ClientNotifyEvent event = new ClientNotifyEvent(method, params); - return event; - } - - @Override - public byte getDataType() { - return TYPE_NOTIFY; - } - - @Override - protected void releaseInternal() { - } - - /** - * @return the method - */ - public String getMethod() { - return method; - } - - /** - * @return the params - */ - public Object[] getParams() { - return params; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "ClientNotifyEvent [method=" + method + ", params=" + Arrays.toString(params) + "]"; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/FlexMessage.java b/src/main/java/org/red5/server/net/rtmp/event/FlexMessage.java deleted file mode 100644 index 450fafb60..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/FlexMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -/** - * Flex method invocation. To be implemented. - */ -public class FlexMessage extends Invoke { - - private static final long serialVersionUID = 1854760132754344723L; - - public FlexMessage() { - super(); - dataType = TYPE_FLEX_MESSAGE; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/FlexStreamSend.java b/src/main/java/org/red5/server/net/rtmp/event/FlexStreamSend.java deleted file mode 100644 index ca96d4413..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/FlexStreamSend.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import org.apache.mina.core.buffer.IoBuffer; - -/** - * AMF3 stream send message. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public class FlexStreamSend extends Notify { - - private static final long serialVersionUID = -4226252245996614504L; - - public FlexStreamSend() { - super(); - dataType = TYPE_FLEX_STREAM_SEND; - } - - /** - * Create new stream send object. - * - * @param data data - */ - public FlexStreamSend(IoBuffer data) { - super(data); - dataType = TYPE_FLEX_STREAM_SEND; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/IRTMPEvent.java b/src/main/java/org/red5/server/net/rtmp/event/IRTMPEvent.java deleted file mode 100644 index 0b9e1dc6e..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/IRTMPEvent.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import org.red5.server.api.event.IEvent; -import org.red5.server.api.event.IEventListener; -import org.red5.server.net.rtmp.message.Header; - -public interface IRTMPEvent extends IEvent { - - /** - * Getter for data type - * - * @return Data type - */ - public byte getDataType(); - - /** - * Setter for source - * - * @param source Source - */ - public void setSource(IEventListener source); - - /** - * Getter for header - * - * @return RTMP packet header - */ - public Header getHeader(); - - /** - * Setter for header - * - * @param header RTMP packet header - */ - public void setHeader(Header header); - - /** - * Getter for timestamp - * - * @return Event timestamp - */ - public int getTimestamp(); - - /** - * Setter for timestamp - * - * @param timestamp New event timestamp - */ - public void setTimestamp(int timestamp); - - /** - * Getter for source type - * - * @return Source type - */ - public byte getSourceType(); - - /** - * Setter for source type - * - * @param sourceType - */ - public void setSourceType(byte sourceType); - - /** - * Retain event - */ - public void retain(); - - /** - * Hook to free buffers allocated by the event. - */ - public void release(); - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/Invoke.java b/src/main/java/org/red5/server/net/rtmp/event/Invoke.java deleted file mode 100644 index 6db7fdf51..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/Invoke.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.service.IPendingServiceCall; - -/** - * Remote invocation event - */ -public class Invoke extends Notify { - - private static final long serialVersionUID = -769677790148010729L; - - /** Constructs a new Invoke. */ - public Invoke() { - super(); - dataType = TYPE_INVOKE; - } - - /** - * Create new invocation event with given data - * @param data Event data - */ - public Invoke(IoBuffer data) { - super(data); - dataType = TYPE_INVOKE; - } - - /** - * Create new invocation event with given pending service call - * @param call Pending call - */ - public Invoke(IPendingServiceCall call) { - super(call); - dataType = TYPE_INVOKE; - } - - /** - * Setter for transaction id - * - * @param transactionId the transactionId to set - */ - public void setTransactionId(int transactionId) { - this.transactionId = transactionId; - } - - /** {@inheritDoc} */ - @Override - public IPendingServiceCall getCall() { - return (IPendingServiceCall) call; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return String.format("Invoke #%d: %s", transactionId, (call != null ? call.toString() : "null")); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (!(obj instanceof Invoke)) { - return false; - } - return super.equals(obj); - } - - /** - * Duplicate this Invoke message to future injection. Serialize to memory and deserialize, safe way. - * - * @return duplicated Invoke event - */ - @Override - public Invoke duplicate() throws IOException, ClassNotFoundException { - Invoke result = new Invoke(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - writeExternal(oos); - oos.close(); - - byte[] buf = baos.toByteArray(); - baos.close(); - - ByteArrayInputStream bais = new ByteArrayInputStream(buf); - ObjectInputStream ois = new ObjectInputStream(bais); - - result.readExternal(ois); - ois.close(); - bais.close(); - - return result; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/Notify.java b/src/main/java/org/red5/server/net/rtmp/event/Notify.java deleted file mode 100644 index c51f65898..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/Notify.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectInputStream; -import java.io.ObjectOutput; -import java.io.ObjectOutputStream; -import java.util.Map; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.service.IServiceCall; -import org.red5.server.api.stream.IStreamPacket; -import org.red5.server.net.ICommand; -import org.red5.server.stream.IStreamData; - -/** - * Stream notification event. The invoke / transaction id is "always" equal to zero for a Notify. - */ -public class Notify extends BaseEvent implements ICommand, IStreamData, IStreamPacket { - - private static final long serialVersionUID = -6085848257275156569L; - - /** - * Service call - */ - protected IServiceCall call; - - /** - * Event data - */ - protected IoBuffer data; - - /** - * Event data type - */ - protected byte dataType = TYPE_NOTIFY; - - /** - * Invoke id / transaction id - */ - protected int transactionId = 0; - - /** - * Connection parameters - */ - private Map connectionParams; - - /** Constructs a new Notify */ - public Notify() { - super(Type.SERVICE_CALL); - } - - /** - * Create new notification event with given byte buffer - * @param data Byte buffer - */ - public Notify(IoBuffer data) { - super(Type.STREAM_DATA); - this.data = data; - } - - /** - * Create new notification event with given service call - * @param call Service call - */ - public Notify(IServiceCall call) { - super(Type.SERVICE_CALL); - this.call = call; - } - - /** {@inheritDoc} */ - public byte getDataType() { - return dataType; - } - - /** - * Setter for data - * - * @param data Data - */ - public void setData(IoBuffer data) { - this.data = data; - } - - /** - * Setter for call - * - * @param call Service call - */ - public void setCall(IServiceCall call) { - this.call = call; - } - - /** - * Getter for service call - * - * @return Service call - */ - public IServiceCall getCall() { - return this.call; - } - - /** {@inheritDoc} */ - public IoBuffer getData() { - return data; - } - - /** - * Getter for transaction id - * - * @return Transaction id - */ - public int getTransactionId() { - return transactionId; - } - - /** - * Release event (nullify call object) - */ - protected void doRelease() { - call = null; - } - - /** - * Getter for connection parameters - * - * @return Connection parameters - */ - public Map getConnectionParams() { - return connectionParams; - } - - /** - * Setter for connection parameters - * - * @param connectionParams Connection parameters - */ - public void setConnectionParams(Map connectionParams) { - this.connectionParams = connectionParams; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Notify: ").append(call); - return sb.toString(); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (!(obj instanceof Notify)) { - return false; - } - Notify other = (Notify) obj; - if (getConnectionParams() == null && other.getConnectionParams() != null) { - return false; - } - if (getConnectionParams() != null && other.getConnectionParams() == null) { - return false; - } - if (getConnectionParams() != null && !getConnectionParams().equals(other.getConnectionParams())) { - return false; - } - if (getTransactionId() != other.getTransactionId()) { - return false; - } - if (getCall() == null && other.getCall() != null) { - return false; - } - if (getCall() != null && other.getCall() == null) { - return false; - } - if (getCall() != null && !getCall().equals(other.getCall())) { - return false; - } - return true; - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - if (data != null) { - data.free(); - data = null; - } - } - - @SuppressWarnings("unchecked") - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - call = (IServiceCall) in.readObject(); - connectionParams = (Map) in.readObject(); - transactionId = in.readInt(); - byte[] byteBuf = (byte[]) in.readObject(); - if (byteBuf != null) { - data = IoBuffer.allocate(0); - data.setAutoExpand(true); - SerializeUtils.ByteArrayToByteBuffer(byteBuf, data); - } - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeObject(call); - out.writeObject(connectionParams); - out.writeInt(transactionId); - if (data != null) { - out.writeObject(SerializeUtils.ByteBufferToByteArray(data)); - } else { - out.writeObject(null); - } - } - - /** - * Duplicate this Notify message to future injection - * Serialize to memory and deserialize, safe way. - * - * @return duplicated Notify event - */ - public Notify duplicate() throws IOException, ClassNotFoundException { - Notify result = new Notify(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - writeExternal(oos); - oos.close(); - - byte[] buf = baos.toByteArray(); - baos.close(); - - ByteArrayInputStream bais = new ByteArrayInputStream(buf); - ObjectInputStream ois = new ObjectInputStream(bais); - - result.readExternal(ois); - ois.close(); - bais.close(); - - return result; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/Ping.java b/src/main/java/org/red5/server/net/rtmp/event/Ping.java deleted file mode 100644 index 5c588eb4c..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/Ping.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -/** - * Ping event, actually combination of different events. This is also known as a user control message. - */ -public class Ping extends BaseEvent { - - private static final long serialVersionUID = -6478248060425544923L; - - /** - * Stream begin / clear event - */ - public static final short STREAM_BEGIN = 0; - - /** - * Stream EOF, playback of requested stream is completed. - */ - public static final short STREAM_PLAYBUFFER_CLEAR = 1; - - /** - * Stream is empty - */ - public static final short STREAM_DRY = 2; - - /** - * Client buffer. Sent by client to indicate its buffer time in milliseconds. - */ - public static final short CLIENT_BUFFER = 3; - - /** - * Recorded stream. Sent by server to indicate a recorded stream. - */ - public static final short RECORDED_STREAM = 4; - - /** - * One more unknown event - */ - public static final short UNKNOWN_5 = 5; - - /** - * Client ping event. Sent by server to test if client is reachable. - */ - public static final short PING_CLIENT = 6; - - /** - * Server response event. A clients ping response. - */ - public static final short PONG_SERVER = 7; - - /** - * One more unknown event - */ - public static final short UNKNOWN_8 = 8; - - /** - * SWF verification ping 0x001a - */ - public static final short PING_SWF_VERIFY = 26; - - /** - * SWF verification pong 0x001b - */ - public static final short PONG_SWF_VERIFY = 27; - - /** - * Buffer empty. - */ - public static final short BUFFER_EMPTY = 31; - - /** - * Buffer full. - */ - public static final short BUFFER_FULL = 32; - - /** - * Event type is undefined - */ - public static final int UNDEFINED = -1; - - /** - * The sub-type - */ - protected short eventType; - - /** - * Represents the stream id in all cases except PING_CLIENT and PONG_SERVER - * where it represents the local server timestamp. - */ - private int value2; - - private int value3 = UNDEFINED; - - private int value4 = UNDEFINED; - - /** - * Debug string - */ - private String debug = ""; - - /** Constructs a new Ping. */ - public Ping() { - super(Type.SYSTEM); - } - - public Ping(short eventType) { - super(Type.SYSTEM); - this.eventType = eventType; - } - - public Ping(short eventType, int value2) { - super(Type.SYSTEM); - this.eventType = eventType; - this.value2 = value2; - } - - public Ping(short eventType, int value2, int value3) { - super(Type.SYSTEM); - this.eventType = eventType; - this.value2 = value2; - this.value3 = value3; - } - - public Ping(short eventType, int value2, int value3, int value4) { - super(Type.SYSTEM); - this.eventType = eventType; - this.value2 = value2; - this.value3 = value3; - this.value4 = value4; - } - - public Ping(Ping in) { - super(Type.SYSTEM); - this.eventType = in.getEventType(); - this.value2 = in.getValue2(); - this.value3 = in.getValue3(); - this.value4 = in.getValue4(); - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return TYPE_PING; - } - - /** - * Returns the events sub-type - * - * @return the event type - */ - public short getEventType() { - return eventType; - } - - /** - * Sets the events sub-type - * - * @param eventType - */ - public void setEventType(short eventType) { - this.eventType = eventType; - } - - /** - * Getter for property 'value2'. - * - * @return Value for property 'value2'. - */ - public int getValue2() { - return value2; - } - - /** - * Setter for property 'value2'. - * - * @param value2 Value to set for property 'value2'. - */ - public void setValue2(int value2) { - this.value2 = value2; - } - - /** - * Getter for property 'value3'. - * - * @return Value for property 'value3'. - */ - public int getValue3() { - return value3; - } - - /** - * Setter for property 'value3'. - * - * @param value3 Value to set for property 'value3'. - */ - public void setValue3(int value3) { - this.value3 = value3; - } - - /** - * Getter for property 'value4'. - * - * @return Value for property 'value4'. - */ - public int getValue4() { - return value4; - } - - /** - * Setter for property 'value4'. - * - * @param value4 Value to set for property 'value4'. - */ - public void setValue4(int value4) { - this.value4 = value4; - } - - /** - * Getter for property 'debug'. - * - * @return Value for property 'debug'. - */ - public String getDebug() { - return debug; - } - - /** - * Setter for property 'debug'. - * - * @param debug Value to set for property 'debug'. - */ - public void setDebug(String debug) { - this.debug = debug; - } - - protected void doRelease() { - eventType = 0; - value2 = 0; - value3 = UNDEFINED; - value4 = UNDEFINED; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "Ping: " + eventType + ", " + value2 + ", " + value3 + ", " + value4 + "\n" + debug; - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - eventType = in.readShort(); - value2 = in.readInt(); - value3 = in.readInt(); - value4 = in.readInt(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeShort(eventType); - out.writeInt(value2); - out.writeInt(value3); - out.writeInt(value4); - } -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/SWFResponse.java b/src/main/java/org/red5/server/net/rtmp/event/SWFResponse.java deleted file mode 100644 index fc549530a..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/SWFResponse.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.red5.server.net.rtmp.event; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; -import java.util.Arrays; - -/** - * Control message used in response to a SWF verification request. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class SWFResponse extends Ping { - - private static final long serialVersionUID = -6478248060425544925L; - - private byte[] bytes; - - public SWFResponse() { - super(); - this.eventType = Ping.PONG_SWF_VERIFY; - } - - public SWFResponse(byte[] bytes) { - this(); - this.bytes = bytes; - } - - /** - * @return the bytes - */ - public byte[] getBytes() { - return bytes; - } - - /** - * @param bytes the bytes to set - */ - public void setBytes(byte[] bytes) { - this.bytes = bytes; - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - eventType = in.readShort(); - if (bytes == null) { - bytes = new byte[42]; - } - in.read(bytes); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeShort(eventType); - out.write(bytes); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "SWFResponse [bytes=" + Arrays.toString(bytes) + "]"; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/SerializeUtils.java b/src/main/java/org/red5/server/net/rtmp/event/SerializeUtils.java deleted file mode 100644 index ff5f2b92c..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/SerializeUtils.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.nio.ByteBuffer; -import org.apache.mina.core.buffer.IoBuffer; - -/** - * The utility class provides conversion methods to ease the use of - * byte arrays, Mina IoBuffers, and NIO ByteBuffers. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class SerializeUtils { - - public static byte[] ByteBufferToByteArray(IoBuffer buf) { - byte[] byteBuf = new byte[buf.limit()]; - int pos = buf.position(); - buf.rewind(); - buf.get(byteBuf); - buf.position(pos); - return byteBuf; - } - - public static byte[] NioByteBufferToByteArray(ByteBuffer buf) { - byte[] byteBuf = new byte[buf.limit()]; - int pos = buf.position(); - buf.position(0); - buf.get(byteBuf); - buf.position(pos); - return byteBuf; - } - - public static void ByteArrayToByteBuffer(byte[] byteBuf, IoBuffer buf) { - buf.put(byteBuf); - buf.flip(); - } - - public static void ByteArrayToNioByteBuffer(byte[] byteBuf, ByteBuffer buf) { - buf.put(byteBuf); - buf.flip(); - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/net/rtmp/event/ServerBW.java b/src/main/java/org/red5/server/net/rtmp/event/ServerBW.java deleted file mode 100644 index 10d96fcb0..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/ServerBW.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -/** - * Server bandwidth event. Also known as a Window Acknowledgement size message. - */ -public class ServerBW extends BaseEvent { - - private static final long serialVersionUID = 24487902555977210L; - - /** - * Bandwidth - */ - private int bandwidth; - - public ServerBW() { - } - - /** - * Server bandwidth event - * @param bandwidth Bandwidth - */ - public ServerBW(int bandwidth) { - super(Type.STREAM_CONTROL); - this.bandwidth = bandwidth; - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return TYPE_SERVER_BANDWIDTH; - } - - /** - * Getter for bandwidth - * - * @return Bandwidth - */ - public int getBandwidth() { - return bandwidth; - } - - /** - * Setter for bandwidth - * - * @param bandwidth New bandwidth. - */ - public void setBandwidth(int bandwidth) { - this.bandwidth = bandwidth; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "ServerBW: " + bandwidth; - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - bandwidth = in.readInt(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeInt(bandwidth); - } -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/SetBuffer.java b/src/main/java/org/red5/server/net/rtmp/event/SetBuffer.java deleted file mode 100644 index 86fc7b633..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/SetBuffer.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.red5.server.net.rtmp.event; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -/** - * Control message used to set a buffer. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class SetBuffer extends Ping { - - private static final long serialVersionUID = -6478248060425544924L; - - private int streamId; - - private int bufferLength; - - public SetBuffer() { - super(); - this.eventType = Ping.CLIENT_BUFFER; - } - - public SetBuffer(int streamId, int bufferLength) { - this(); - this.streamId = streamId; - this.bufferLength = bufferLength; - } - - /** - * @return the streamId - */ - public int getStreamId() { - return streamId; - } - - /** - * @param streamId the streamId to set - */ - public void setStreamId(int streamId) { - this.streamId = streamId; - } - - /** - * @return the bufferLength - */ - public int getBufferLength() { - return bufferLength; - } - - /** - * @param bufferLength the bufferLength to set - */ - public void setBufferLength(int bufferLength) { - this.bufferLength = bufferLength; - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - eventType = in.readShort(); - streamId = in.readInt(); - bufferLength = in.readInt(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeShort(eventType); - out.writeInt(streamId); - out.writeInt(bufferLength); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "SetBuffer [streamId=" + streamId + ", bufferLength=" + bufferLength + "]"; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/StreamActionEvent.java b/src/main/java/org/red5/server/net/rtmp/event/StreamActionEvent.java deleted file mode 100644 index bf9cfec79..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/StreamActionEvent.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.red5.server.net.rtmp.event; - -import org.red5.io.object.StreamAction; -import org.red5.server.api.event.IEvent; -import org.red5.server.api.event.IEventListener; - -/** - * Represents a stream action occurring on a connection or stream. This event is used to notify an IEventHandler; it is not - * meant to be sent over the wire to clients. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class StreamActionEvent implements IEvent { - - private final StreamAction action; - - public StreamActionEvent(StreamAction action) { - this.action = action; - } - - public Type getType() { - return Type.STREAM_ACTION; - } - - public Object getObject() { - return action; - } - - public boolean hasSource() { - return false; - } - - public IEventListener getSource() { - return null; - } - - @Override - public String toString() { - return "StreamActionEvent [action=" + action + "]"; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/Unknown.java b/src/main/java/org/red5/server/net/rtmp/event/Unknown.java deleted file mode 100644 index 2befb43b2..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/Unknown.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.io.utils.HexDump; - -/** - * Unknown event - */ -public class Unknown extends BaseEvent { - private static final long serialVersionUID = -1352770037962252975L; - /** - * Event data - */ - protected IoBuffer data; - /** - * Type of data - */ - protected byte dataType; - - public Unknown() {} - /** - * Create new unknown event with given data and data type - * @param dataType Data type - * @param data Event data - */ - public Unknown(byte dataType, IoBuffer data) { - super(Type.SYSTEM); - this.dataType = dataType; - this.data = data; - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return dataType; - } - - /** - * Getter for data - * - * @return Data - */ - public IoBuffer getData() { - return data; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - final IoBuffer buf = getData(); - StringBuffer sb = new StringBuffer(); - sb.append("Size: "); - sb.append(buf.remaining()); - sb.append(" Data:\n\n"); - sb.append(HexDump.formatHexDump(buf.getHexDump())); - return sb.toString(); - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - if (data != null) { - data.free(); - data = null; - } - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - dataType = in.readByte(); - byte[] byteBuf = (byte[]) in.readObject(); - if (byteBuf != null) { - data = IoBuffer.allocate(0); - data.setAutoExpand(true); - SerializeUtils.ByteArrayToByteBuffer(byteBuf, data); - } - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeByte(dataType); - if (data != null) { - out.writeObject(SerializeUtils.ByteBufferToByteArray(data)); - } else { - out.writeObject(null); - } - } -} diff --git a/src/main/java/org/red5/server/net/rtmp/event/VideoData.java b/src/main/java/org/red5/server/net/rtmp/event/VideoData.java deleted file mode 100644 index 25e9969c7..000000000 --- a/src/main/java/org/red5/server/net/rtmp/event/VideoData.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.event; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectInputStream; -import java.io.ObjectOutput; -import java.io.ObjectOutputStream; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.io.IoConstants; -import org.red5.server.api.stream.IStreamPacket; -import org.red5.server.stream.IStreamData; - -/** - * Video data event - */ -public class VideoData extends BaseEvent implements IoConstants, IStreamData, IStreamPacket { - - private static final long serialVersionUID = 5538859593815804830L; - - /** - * Videoframe type - */ - public static enum FrameType { - UNKNOWN, KEYFRAME, INTERFRAME, DISPOSABLE_INTERFRAME, - } - - /** - * Video data - */ - protected IoBuffer data; - - /** - * Data type - */ - private byte dataType = TYPE_VIDEO_DATA; - - /** - * Frame type, unknown by default - */ - protected FrameType frameType = FrameType.UNKNOWN; - - /** Constructs a new VideoData. */ - public VideoData() { - this(IoBuffer.allocate(0).flip()); - } - - /** - * Create video data event with given data buffer - * @param data Video data - */ - public VideoData(IoBuffer data) { - super(Type.STREAM_DATA); - setData(data); - } - - /** - * Create video data event with given data buffer - * @param data Video data - * @param copy true to use a copy of the data or false to use reference - */ - public VideoData(IoBuffer data, boolean copy) { - super(Type.STREAM_DATA); - if (copy) { - byte[] array = new byte[data.limit()]; - data.mark(); - data.get(array); - data.reset(); - setData(array); - } else { - setData(data); - } - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return dataType; - } - - public void setDataType(byte dataType) { - this.dataType = dataType; - } - - /** {@inheritDoc} */ - public IoBuffer getData() { - return data; - } - - public void setData(IoBuffer data) { - this.data = data; - if (data != null && data.limit() > 0) { - data.mark(); - int firstByte = (data.get(0)) & 0xff; - data.reset(); - int frameType = (firstByte & MASK_VIDEO_FRAMETYPE) >> 4; - if (frameType == FLAG_FRAMETYPE_KEYFRAME) { - this.frameType = FrameType.KEYFRAME; - } else if (frameType == FLAG_FRAMETYPE_INTERFRAME) { - this.frameType = FrameType.INTERFRAME; - } else if (frameType == FLAG_FRAMETYPE_DISPOSABLE) { - this.frameType = FrameType.DISPOSABLE_INTERFRAME; - } else { - this.frameType = FrameType.UNKNOWN; - } - } - } - - public void setData(byte[] data) { - this.data = IoBuffer.allocate(data.length); - this.data.put(data).flip(); - } - - /** - * Getter for frame type - * - * @return Type of video frame - */ - public FrameType getFrameType() { - return frameType; - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - if (data != null) { - final IoBuffer localData = data; - // null out the data first so we don't accidentally - // return a valid reference first - data = null; - localData.clear(); - localData.free(); - } - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - frameType = (FrameType) in.readObject(); - byte[] byteBuf = (byte[]) in.readObject(); - if (byteBuf != null) { - data = IoBuffer.allocate(byteBuf.length); - data.setAutoExpand(true); - SerializeUtils.ByteArrayToByteBuffer(byteBuf, data); - } - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeObject(frameType); - if (data != null) { - out.writeObject(SerializeUtils.ByteBufferToByteArray(data)); - } else { - out.writeObject(null); - } - } - - /** - * Duplicate this message / event. - * - * @return duplicated event - */ - public VideoData duplicate() throws IOException, ClassNotFoundException { - VideoData result = new VideoData(); - // serialize - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - writeExternal(oos); - oos.close(); - // convert to byte array - byte[] buf = baos.toByteArray(); - baos.close(); - // create input streams - ByteArrayInputStream bais = new ByteArrayInputStream(buf); - ObjectInputStream ois = new ObjectInputStream(bais); - // deserialize - result.readExternal(ois); - ois.close(); - bais.close(); - // clone the header if there is one - if (header != null) { - result.setHeader(header.clone()); - } - return result; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return String.format("Video - ts: %s length: %s", getTimestamp(), (data != null ? data.limit() : '0')); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/message/Constants.java b/src/main/java/org/red5/server/net/rtmp/message/Constants.java deleted file mode 100644 index df263c8f5..000000000 --- a/src/main/java/org/red5/server/net/rtmp/message/Constants.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.message; - -/** - * Class for AMF and RTMP marker values constants - */ -public interface Constants { - - /** - * Data originated from a file. - */ - public static final byte SOURCE_TYPE_VOD = 0x0; - - /** - * Data originated from a live encoder or stream. - */ - public static final byte SOURCE_TYPE_LIVE = 0x01; - - /** - * Medium integer max value - */ - public static final int MEDIUM_INT_MAX = 16777215; - - /** - * RTMP chunk size constant - */ - public static final byte TYPE_CHUNK_SIZE = 0x01; - - /** - * Abort message - */ - public static final byte TYPE_ABORT = 0x02; - - /** - * Acknowledgment. Send every x bytes read by both sides. - */ - public static final byte TYPE_BYTES_READ = 0x03; - - /** - * Ping is a stream control message, it has sub-types - */ - public static final byte TYPE_PING = 0x04; - - /** - * Server (downstream) bandwidth marker - */ - public static final byte TYPE_SERVER_BANDWIDTH = 0x05; - - /** - * Client (upstream) bandwidth marker - */ - public static final byte TYPE_CLIENT_BANDWIDTH = 0x06; - - /** - * Edge / Origin message. - */ - public static final byte TYPE_EDGE_ORIGIN = 0x07; - - /** - * Audio data marker - */ - public static final byte TYPE_AUDIO_DATA = 0x08; - - /** - * Video data marker - */ - public static final byte TYPE_VIDEO_DATA = 0x09; - - // Unknown: 0x0A ... 0x0E - - /** - * AMF3 stream send - */ - public static final byte TYPE_FLEX_STREAM_SEND = 0x0F; - - /** - * AMF3 shared object - */ - public static final byte TYPE_FLEX_SHARED_OBJECT = 0x10; - - /** - * AMF3 message - */ - public static final byte TYPE_FLEX_MESSAGE = 0x11; - - /** - * Notification is invocation without response - */ - public static final byte TYPE_NOTIFY = 0x12; - - /** - * Stream metadata - */ - public static final byte TYPE_STREAM_METADATA = 0x12; - - /** - * Shared Object marker - */ - public static final byte TYPE_SHARED_OBJECT = 0x13; - - /** - * Invoke operation (remoting call but also used for streaming) marker - */ - public static final byte TYPE_INVOKE = 0x14; - - /** - * Aggregate data marker - */ - public static final byte TYPE_AGGREGATE = 0x16; - - /** - * New header marker - */ - public static final byte HEADER_NEW = 0x00; - - /** - * Same source marker - */ - public static final byte HEADER_SAME_SOURCE = 0x01; - - /** - * Timer change marker - */ - public static final byte HEADER_TIMER_CHANGE = 0x02; - - /** - * There's more to encode - */ - public static final byte HEADER_CONTINUE = 0x03; - - /** - * Size of initial handshake between client and server - */ - public static final int HANDSHAKE_SIZE = 1536; - - /** - * Client Shared Object data update - */ - public static final byte SO_CLIENT_UPDATE_DATA = 0x04; //update data - - /** - * Client Shared Object attribute update - */ - public static final byte SO_CLIENT_UPDATE_ATTRIBUTE = 0x05; //5: update attribute - - /** - * Send SO message flag - */ - public static final byte SO_CLIENT_SEND_MESSAGE = 0x06; // 6: send message - - /** - * Shared Object status marker - */ - public static final byte SO_CLIENT_STATUS = 0x07; // 7: status (usually returned with error messages) - - /** - * Clear event for Shared Object - */ - public static final byte SO_CLIENT_CLEAR_DATA = 0x08; // 8: clear data - - /** - * Delete data for Shared Object - */ - public static final byte SO_CLIENT_DELETE_DATA = 0x09; // 9: delete data - - /** - * Initial SO data flag - */ - public static final byte SO_CLIENT_INITIAL_DATA = 0x0B; // 11: initial data - - /** - * Shared Object connection - */ - public static final byte SO_CONNECT = 0x01; - - /** - * Shared Object disconnection - */ - public static final byte SO_DISCONNECT = 0x02; - - /** - * Set Shared Object attribute flag - */ - public static final byte SO_SET_ATTRIBUTE = 0x03; - - /** - * Send message flag - */ - public static final byte SO_SEND_MESSAGE = 0x06; - - /** - * Shared Object attribute deletion flag - */ - public static final byte SO_DELETE_ATTRIBUTE = 0x0A; - -} diff --git a/src/main/java/org/red5/server/net/rtmp/message/Header.java b/src/main/java/org/red5/server/net/rtmp/message/Header.java deleted file mode 100644 index 3a58a8ce4..000000000 --- a/src/main/java/org/red5/server/net/rtmp/message/Header.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.message; - -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -/** - * RTMP packet header - */ -public class Header implements Constants, Cloneable, Externalizable { - - private static final long serialVersionUID = 8982665579411495024L; - - /** - * Channel - */ - private int channelId; - - /** - * Timer - */ - private int timerBase; - - /** - * Delta - */ - private int timerDelta; - - /** - * Header size - */ - private int size; - - /** - * Type of data - */ - private byte dataType; - - /** - * Stream id - */ - private int streamId; - - /** - * Extended Timestamp - */ - private int extendedTimestamp; - - /** - * Getter for channel id - * - * @return Channel id - */ - public int getChannelId() { - return channelId; - } - - /** - * Setter for channel id - * - * @param channelId Header channel id - */ - public void setChannelId(int channelId) { - this.channelId = channelId; - } - - /** - * Getter for data type - * - * @return Data type - */ - public byte getDataType() { - return dataType; - } - - /** - * Setter for data type - * - * @param dataType Data type - */ - public void setDataType(byte dataType) { - this.dataType = dataType; - } - - /** - * Getter for size. - * - * @return Header size - */ - public int getSize() { - return size; - } - - /** - * Setter for size - * - * @param size Header size - */ - public void setSize(int size) { - this.size = size; - } - - /** - * Getter for stream id - * - * @return Stream id - */ - public int getStreamId() { - return streamId; - } - - /** - * Setter for stream id - * - * @param streamId Stream id - */ - public void setStreamId(int streamId) { - this.streamId = streamId; - } - - /** - * Getter for Extended Timestamp - * - * @return Extended Timestamp - */ - public int getExtendedTimestamp() { - return extendedTimestamp; - } - - /** - * Setter for Extended Timestamp - * - * @param extendedTimestamp Extended Timestamp - */ - public void setExtendedTimestamp(int extendedTimestamp) { - this.extendedTimestamp = extendedTimestamp; - } - - /** - * Getter for timer - * - * @return Timer - */ - public int getTimer() { - return timerBase + timerDelta; - } - - /** - * Setter for timer - * - * @param timer Timer - */ - public void setTimer(int timer) { - this.timerBase = timer; - this.timerDelta = 0; - } - - public void setTimerDelta(int timerDelta) { - this.timerDelta = timerDelta; - } - - public int getTimerDelta() { - return timerDelta; - } - - public void setTimerBase(int timerBase) { - this.timerBase = timerBase; - } - - public int getTimerBase() { - return timerBase; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof Header)) { - return false; - } - final Header header = (Header) other; - return (header.getChannelId() == channelId && header.getDataType() == dataType && header.getSize() == size && header.getTimer() == this.getTimer() && header.getStreamId() == streamId && header.getExtendedTimestamp() == extendedTimestamp); - } - - /** {@inheritDoc} */ - @Override - public Header clone() { - final Header header = new Header(); - header.setChannelId(channelId); - header.setTimerBase(timerBase); - header.setTimerDelta(timerDelta); - header.setSize(size); - header.setDataType(dataType); - header.setStreamId(streamId); - header.setExtendedTimestamp(extendedTimestamp); - return header; - } - - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - dataType = in.readByte(); - channelId = in.readInt(); - size = in.readInt(); - streamId = in.readInt(); - timerBase = in.readInt(); - timerDelta = in.readInt(); - extendedTimestamp = in.readInt(); - } - - public void writeExternal(ObjectOutput out) throws IOException { - out.writeByte(dataType); - out.writeInt(channelId); - out.writeInt(size); - out.writeInt(streamId); - out.writeInt(timerBase); - out.writeInt(timerDelta); - out.writeInt(extendedTimestamp); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "Header [channelId=" + channelId + ", dataType=" + dataType + ", timerBase=" + timerBase + ", timerDelta=" + timerDelta + ", size=" + size + ", streamId=" - + streamId + ", extendedTimestamp=" + extendedTimestamp + "]"; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/message/Packet.java b/src/main/java/org/red5/server/net/rtmp/message/Packet.java deleted file mode 100644 index e00a4a62a..000000000 --- a/src/main/java/org/red5/server/net/rtmp/message/Packet.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.message; - -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RTMP packet. Consists of packet header, data and event context. - */ -public class Packet implements Externalizable { - - private static final long serialVersionUID = -6415050845346626950L; - - private static Logger log = LoggerFactory.getLogger(Packet.class); - - private static final boolean noCopy = System.getProperty("packet.noCopy") == null ? false : Boolean.valueOf(System.getProperty("packet.noCopy")); - - /** - * Header - */ - private Header header; - - /** - * RTMP event - */ - private IRTMPEvent message; - - /** - * Packet data - */ - private IoBuffer data; - - public Packet() { - log.trace("ctor"); - } - - /** - * Create packet with given header - * @param header Packet header - */ - public Packet(Header header) { - log.trace("Header: {}", header); - this.header = header; - data = IoBuffer.allocate(header.getSize()).setAutoExpand(true); - } - - /** - * Create packet with given header and event context - * @param header RTMP header - * @param event RTMP message - */ - public Packet(Header header, IRTMPEvent event) { - log.trace("Header: {} event: {}", header, event); - this.header = header; - this.message = event; - } - - /** - * Getter for header - * - * @return Packet header - */ - public Header getHeader() { - return header; - } - - /** - * Setter for event context - * - * @param message RTMP event context - */ - public void setMessage(IRTMPEvent message) { - this.message = message; - } - - /** - * Getter for event context - * - * @return RTMP event context - */ - public IRTMPEvent getMessage() { - return message; - } - - /** - * Setter for data - * - * @param buffer Packet data - */ - public void setData(IoBuffer buffer) { - if (noCopy) { - log.trace("Using buffer reference"); - this.data = buffer; - } else { - // try the backing array first if it exists - if (buffer.hasArray()) { - log.trace("Buffer has backing array, making a copy"); - byte[] copy = new byte[buffer.limit()]; - buffer.mark(); - buffer.get(copy); - buffer.reset(); - this.data = IoBuffer.wrap(copy); - } else { - log.trace("Buffer has no backing array, using ByteBuffer"); - // fallback to ByteBuffer - this.data.put(buffer.buf()).flip(); - } - } - } - - /** - * Getter for data - * - * @return Packet data - */ - public IoBuffer getData() { - return data; - } - - /** - * Returns whether or not the packet has a data buffer. - * - * @return true if data buffer exists and false otherwise - */ - public boolean hasData() { - return data != null; - } - - /** - * Clears the data buffer. - */ - public void clearData() { - if (data != null) { - data.clear(); - data.free(); - data = null; - } - } - - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - header = (Header) in.readObject(); - message = (IRTMPEvent) in.readObject(); - message.setHeader(header); - message.setTimestamp(header.getTimer()); - } - - public void writeExternal(ObjectOutput out) throws IOException { - out.writeObject(header); - out.writeObject(message); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - StringBuilder sb = new StringBuilder("Packet ["); - if (header != null) { - sb.append("[header data type=" + header.getDataType()+ ", channel=" + header.getChannelId() + ", timer=" + header.getTimer() + "]"); - } else { - sb.append("[header=null]"); - } - if (message != null) { - sb.append(", [message timestamp=" + message.getTimestamp() + "]"); - } else { - sb.append(", [message=null]"); - } - sb.append("]"); - return sb.toString(); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmp/message/SharedObjectTypeMapping.java b/src/main/java/org/red5/server/net/rtmp/message/SharedObjectTypeMapping.java deleted file mode 100644 index 5f8a9697f..000000000 --- a/src/main/java/org/red5/server/net/rtmp/message/SharedObjectTypeMapping.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.message; - -import org.red5.server.so.ISharedObjectEvent.Type; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * SO event types mapping - */ -public class SharedObjectTypeMapping { - - protected static Logger log = LoggerFactory.getLogger(SharedObjectTypeMapping.class); - - /** - * Types map - */ - public static final Type[] typeMap = new Type[] { null, - Type.SERVER_CONNECT, // 01 - Type.SERVER_DISCONNECT, // 02 - Type.SERVER_SET_ATTRIBUTE, // 03 - Type.CLIENT_UPDATE_DATA, // 04 - Type.CLIENT_UPDATE_ATTRIBUTE, // 05 - Type.SERVER_SEND_MESSAGE, // 06 - Type.CLIENT_STATUS, // 07 - Type.CLIENT_CLEAR_DATA, // 08 - Type.CLIENT_DELETE_DATA, // 09 - Type.SERVER_DELETE_ATTRIBUTE, // 0A - Type.CLIENT_INITIAL_DATA, // 0B - }; - - /** - * Convert byte value of RTMP marker to event type - * @param rtmpType RTMP marker value - * @return Corresponding Shared Object event type - */ - public static Type toType(byte rtmpType) { - return typeMap[rtmpType]; - } - - /** - * Convert SO event type to byte representation that RTMP uses - * @param type Event type - * @return Byte representation of given event type - */ - public static byte toByte(Type type) { - switch (type) { - case SERVER_CONNECT: - return 0x01; - case SERVER_DISCONNECT: - return 0x02; - case SERVER_SET_ATTRIBUTE: - return 0x03; - case CLIENT_UPDATE_DATA: - return 0x04; - case CLIENT_UPDATE_ATTRIBUTE: - return 0x05; - case SERVER_SEND_MESSAGE: - return 0x06; - case CLIENT_SEND_MESSAGE: - return 0x06; - case CLIENT_STATUS: - return 0x07; - case CLIENT_CLEAR_DATA: - return 0x08; - case CLIENT_DELETE_DATA: - return 0x09; - case SERVER_DELETE_ATTRIBUTE: - return 0x0A; - case CLIENT_INITIAL_DATA: - return 0x0B; - default: - log.error("Unknown type " + type); - return 0x00; - } - } - - /** - * String representation of type - * @param type Type - * @return String representation of type - */ - public static String toString(Type type) { - switch (type) { - case SERVER_CONNECT: - return "server connect"; - case SERVER_DISCONNECT: - return "server disconnect"; - case SERVER_SET_ATTRIBUTE: - return "server_set_attribute"; - case CLIENT_UPDATE_DATA: - return "client_update_data"; - case CLIENT_UPDATE_ATTRIBUTE: - return "client_update_attribute"; - case SERVER_SEND_MESSAGE: - return "server_send_message"; - case CLIENT_SEND_MESSAGE: - return "client_send_message"; - case CLIENT_STATUS: - return "client_status"; - case CLIENT_CLEAR_DATA: - return "client_clear_data"; - case CLIENT_DELETE_DATA: - return "client_delete_data"; - case SERVER_DELETE_ATTRIBUTE: - return "server_delete_attribute"; - case CLIENT_INITIAL_DATA: - return "client_initial_data"; - default: - log.error("Unknown type " + type); - return "unknown"; - } - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/net/rtmp/status/RuntimeStatusObject.java b/src/main/java/org/red5/server/net/rtmp/status/RuntimeStatusObject.java deleted file mode 100644 index e179dd8e5..000000000 --- a/src/main/java/org/red5/server/net/rtmp/status/RuntimeStatusObject.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.status; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -/** - * Runtime status object - */ -public class RuntimeStatusObject extends StatusObject { - - /** - * Serializable - */ - private static final long serialVersionUID = 6990998992583246039L; - /** - * Status event details - */ - protected String details = ""; - /** - * Client id - */ - protected int clientid = 0; - - /** Constructs a new RuntimeStatusObject. */ - public RuntimeStatusObject() { - super(); - } - - /** - * Create runtime status object with given code, level and description - * @param code Status code - * @param level Level - * @param description Status event description - */ - public RuntimeStatusObject(String code, String level, String description) { - super(code, level, description); - } - - /** - * Create runtime status object with given code, level, description, details and client id - * @param code Status code - * @param level Level - * @param description Status event description - * @param details Status event details - * @param clientid Client id - */ - public RuntimeStatusObject(String code, String level, String description, - String details, int clientid) { - super(code, level, description); - this.details = details; - this.clientid = clientid; - } - - /** - * Getter for client id - * - * @return Client id - */ - public int getClientid() { - return clientid; - } - - /** - * Setter for client id - * - * @param clientid Client id - */ - public void setClientid(int clientid) { - this.clientid = clientid; - } - - /** - * Getter for details - * - * @return Status event details - */ - public String getDetails() { - return details; - } - - /** - * Setter for details - * - * @param details Status event details - */ - public void setDetails(String details) { - this.details = details; - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - clientid = in.readInt(); - details = (String) in.readObject(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeInt(clientid); - out.writeObject(details); - } -} diff --git a/src/main/java/org/red5/server/net/rtmp/status/Status.java b/src/main/java/org/red5/server/net/rtmp/status/Status.java deleted file mode 100644 index 8c28cc9c4..000000000 --- a/src/main/java/org/red5/server/net/rtmp/status/Status.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.status; - -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -import org.red5.annotations.Anonymous; -import org.red5.io.object.ICustomSerializable; -import org.red5.io.object.Output; - -/** - * Represents status object that are transferred between server and client - */ -@Anonymous -public class Status implements StatusCodes, ICustomSerializable, Externalizable { - private static final long serialVersionUID = -5501563718489586136L; - - /** - * Error constant - */ - public static final String ERROR = "error"; - - /** - * Status constant - */ - public static final String STATUS = "status"; - - /** - * Warning constant - */ - public static final String WARNING = "warning"; - - /** - * Status code - */ - protected String code; - - /** - * Status level - */ - protected String level; - - /** - * Status event description - */ - protected String description = ""; - - /** - * Status event details - */ - protected String details = ""; - - /** - * Id of client - */ - protected int clientid; - - /** Constructs a new Status. */ - public Status() { - } - - /** - * Creates status object with given status code - * @param code Status code - */ - public Status(String code) { - this.code = code; - this.level = STATUS; - } - - /** - * Creates status object with given level, description and status code - * @param code Status code - * @param level Level - * @param description Description - */ - public Status(String code, String level, String description) { - this.code = code; - this.level = level; - this.description = description; - } - - /** - * Getter for status code. - * - * @return Status code - */ - public String getCode() { - return code; - } - - /** - * Setter for code - * - * @param code Status code - */ - public void setCode(String code) { - this.code = code; - } - - /** - * Getter for description. - * - * @return Status event description. - */ - public String getDescription() { - return description; - } - - /** - * Setter for description. - * - * @param description Status event description. - */ - public void setDesciption(String description) { - this.description = description; - } - - /** - * Getter for level. - * - * @return Level - */ - public String getLevel() { - return level; - } - - /** - * Setter for level - * - * @param level Level - */ - public void setLevel(String level) { - this.level = level; - } - - /** - * Getter for client id - * - * @return Client id - */ - public int getClientid() { - return clientid; - } - - /** - * Setter for client id - * - * @param clientid Client id - */ - public void setClientid(int clientid) { - this.clientid = clientid; - } - - /** - * Getter for details - * - * @return Status event details - */ - public String getDetails() { - return details; - } - - /** - * Setter for details. - * - * @param details Status event details - */ - public void setDetails(String details) { - this.details = details; - } - - /** - * Setter for description. - * - * @param description Status event description - */ - public void setDescription(String description) { - this.description = description; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "Status: code: " + getCode() + " desc: " + getDescription() + " level: " + getLevel(); - } - - public void serialize(Output output) { - output.putString("level"); - output.writeString(getLevel()); - output.putString("code"); - output.writeString(getCode()); - output.putString("description"); - output.writeString(getDescription()); - output.putString("details"); - if (getDetails() != null) { - output.writeString(getDetails()); - } else { - output.writeNull(); - } - output.putString("clientid"); - output.writeNumber(getClientid()); - } - - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - clientid = in.readInt(); - code = (String) in.readObject(); - description = (String) in.readObject(); - details = (String) in.readObject(); - level = (String) in.readObject(); - } - - public void writeExternal(ObjectOutput out) throws IOException { - out.writeInt(clientid); - out.writeObject(code); - out.writeObject(description); - out.writeObject(details); - out.writeObject(level); - } -} diff --git a/src/main/java/org/red5/server/net/rtmp/status/StatusCodes.java b/src/main/java/org/red5/server/net/rtmp/status/StatusCodes.java deleted file mode 100644 index 05a1901ec..000000000 --- a/src/main/java/org/red5/server/net/rtmp/status/StatusCodes.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.status; - -/** - * Collection of commonly used constants with status codes. Descriptions provided as in FMS 2.0.1 - * documentation available at adobe.com with some minor additions and comments. - */ -public interface StatusCodes { - /** - * The NetConnection.call method was not able to invoke the server-side method or - * command. - */ - public static final String NC_CALL_FAILED = "NetConnection.Call.Failed"; - - /** - * The URI specified in the NetConnection.connect method did not - * specify 'rtmp' as the protocol. 'rtmp' must be specified when connecting to - * FMS and Red5. Either not supported version of AMF was used (3 when only 0 is supported) - */ - public static final String NC_CALL_BADVERSION = "NetConnection.Call.BadVersion"; - - /** - * The application has been shut down (for example, if the application is out of - * memory resources and must shut down to prevent the server from crashing) or the server has shut down. - */ - public static final String NC_CONNECT_APPSHUTDOWN = "NetConnection.Connect.AppShutdown"; - - /** - * The connection was closed successfully - */ - public static final String NC_CONNECT_CLOSED = "NetConnection.Connect.Closed"; - - /** - * The connection attempt failed. - */ - public static final String NC_CONNECT_FAILED = "NetConnection.Connect.Failed"; - - /** - * The client does not have permission to connect to the application, the - * application expected different parameters from those that were passed, - * or the application name specified during the connection attempt was not found on - * the server. - */ - public static final String NC_CONNECT_REJECTED = "NetConnection.Connect.Rejected"; - - /** - * The connection attempt succeeded. - */ - public static final String NC_CONNECT_SUCCESS = "NetConnection.Connect.Success"; - - /** - * The application name specified during connect is invalid. - */ - public static final String NC_CONNECT_INVALID_APPLICATION = "NetConnection.Connect.InvalidApp"; - - /** - * Invalid arguments were passed to a NetStream method. - */ - public static final String NS_INVALID_ARGUMENT = "NetStream.InvalidArg"; - - /** - * A recorded stream was deleted successfully. - */ - public static final String NS_CLEAR_SUCCESS = "NetStream.Clear.Success"; - - /** - * A recorded stream failed to delete. - */ - public static final String NS_CLEAR_FAILED = "NetStream.Clear.Failed"; - - /** - * An attempt to publish was successful. - */ - public static final String NS_PUBLISH_START = "NetStream.Publish.Start"; - - /** - * An attempt was made to publish a stream that is already being published by someone else. - */ - public static final String NS_PUBLISH_BADNAME = "NetStream.Publish.BadName"; - - /** - * An attempt to use a Stream method (at client-side) failed - */ - public static final String NS_FAILED = "NetStream.Failed"; - - /** - * An attempt to unpublish was successful - */ - public static final String NS_UNPUBLISHED_SUCCESS = "NetStream.Unpublish.Success"; - - /** - * Recording was started - */ - public static final String NS_RECORD_START = "NetStream.Record.Start"; - - /** - * An attempt was made to record a read-only stream - */ - public static final String NS_RECORD_NOACCESS = "NetStream.Record.NoAccess"; - - /** - * Recording was stopped - */ - public static final String NS_RECORD_STOP = "NetStream.Record.Stop"; - - /** - * An attempt to record a stream failed - */ - public static final String NS_RECORD_FAILED = "NetStream.Record.Failed"; - - /** - * The buffer is empty (sent from server to client) - */ - public static final String NS_BUFFER_EMPTY = "NetStream.Buffer.Empty"; - - /** - * Data is playing behind the normal speed - */ - public static final String NS_PLAY_INSUFFICIENT_BW = "NetStream.Play.InsufficientBW"; - - /** - * Play was started - */ - public static final String NS_PLAY_START = "NetStream.Play.Start"; - - /** - * An attempt was made to play a stream that does not exist - */ - public static final String NS_PLAY_STREAMNOTFOUND = "NetStream.Play.StreamNotFound"; - - /** - * Play was stopped - */ - public static final String NS_PLAY_STOP = "NetStream.Play.Stop"; - - /** - * An attempt to play back a stream failed - */ - public static final String NS_PLAY_FAILED = "NetStream.Play.Failed"; - - /** - * A playlist was reset - */ - public static final String NS_PLAY_RESET = "NetStream.Play.Reset"; - - /** - * The initial publish to a stream was successful. This message is sent to all subscribers - */ - public static final String NS_PLAY_PUBLISHNOTIFY = "NetStream.Play.PublishNotify"; - - /** - * An unpublish from a stream was successful. This message is sent to all subscribers - */ - public static final String NS_PLAY_UNPUBLISHNOTIFY = "NetStream.Play.UnpublishNotify"; - - /** - * Playlist playback switched from one stream to another. - */ - public static final String NS_PLAY_SWITCH = "NetStream.Play.Switch"; - - /** - * Transition to another stream has been initiated. - */ - public static final String NS_PLAY_TRANSITION = "NetStream.Play.Transition"; - - /** - * Transition to another stream is processing normally. This is set as the "reason" property of the NetStatusEvent. - */ - public static final String NS_TRANSITION_SUCCESS = "NetStream.Transition.Success"; - - /** - * Transition to another stream has been forced. - * - * If streams that are being switched do not have aligned content/timelines, or if the keyframes are not aligned between - * the two streams, it is possible that the server will have to force a hard transition. This can also happen with - * broadcast (live) dynamic streaming if the server cannot find a synchronization point within the two streams. - * This is set as the "reason" property of the NetStatusEvent. - */ - public static final String NS_TRANSITION_FORCED = "NetStream.Transition.Forced"; - - /** - * Transition to another stream is complete. - */ - public static final String NS_PLAY_TRANSITION_COMPLETE = "NetStream.Play.TransitionComplete"; - - /** - * Playlist playback is complete. - */ - public static final String NS_PLAY_COMPLETE = "NetStream.Play.Complete"; - - /** - * The subscriber has used the seek command to move to a particular location in the recorded stream. - */ - public static final String NS_SEEK_NOTIFY = "NetStream.Seek.Notify"; - - /** - * The stream doesn't support seeking. - */ - public static final String NS_SEEK_FAILED = "NetStream.Seek.Failed"; - - /** - * The subscriber has used the seek command to move to a particular location in the recorded stream. - */ - public static final String NS_PAUSE_NOTIFY = "NetStream.Pause.Notify"; - - /** - * Publishing has stopped - */ - public static final String NS_UNPAUSE_NOTIFY = "NetStream.Unpause.Notify"; - - /** - * - */ - public static final String NS_DATA_START = "NetStream.Data.Start"; - - /** - * The ActionScript engine has encountered a runtime error. In addition to the standard infoObject - * properties, the following properties are set: - * - * filename: name of the offending ASC file. - * lineno: line number where the error occurred. - * linebuf: source code of the offending line. - */ - public static final String APP_SCRIPT_ERROR = "Application.Script.Error"; - - /** - * The ActionScript engine has encountered a runtime warning. In addition to the standard infoObject - * properties, the following properties are set: - * - * filename: name of the offending ASC file. - * lineno: line number where the error occurred. - * linebuf: source code of the offending line - */ - public static final String APP_SCRIPT_WARNING = "Application.Script.Warning"; - - /** - * The ActionScript engine is low on runtime memory. This provides an opportunity for the - * application instance to free some resources or take suitable action. If the application instance - * runs out of memory, it is unloaded and all users are disconnected. In this state, the server will - * not invoke the Application.onDisconnect event handler or the Application.onAppStop event handler - */ - public static final String APP_RESOURCE_LOWMEMORY = "Application.Resource.LowMemory"; - - /** - * This information object is passed to the onAppStop handler when the application is being shut down - */ - public static final String APP_SHUTDOWN = "Application.Shutdown"; - - /** - * This information object is passed to the onAppStop event handler when the application instance - * is about to be destroyed by the server. - */ - public static final String APP_GC = "Application.GC"; - - /** - * Read access to a shared object was denied. - */ - public static final String SO_NO_READ_ACCESS = "SharedObject.NoReadAccess"; - - /** - * Write access to a shared object was denied. - */ - public static final String SO_NO_WRITE_ACCESS = "SharedObject.NoWriteAccess"; - - /** - * The creation of a shared object was denied. - */ - public static final String SO_CREATION_FAILED = "SharedObject.ObjectCreationFailed"; - - /** - * The persistence parameter passed to SharedObject.getRemote() is different from the one used - * when the shared object was created. - */ - public static final String SO_PERSISTENCE_MISMATCH = "SharedObject.BadPersistence"; - - /** - * This event is sent if the player detects an MP4 with an invalid file structure. - * Flash Player cannot play files that have invalid file structures. - */ - public static final String NS_PLAY_FILE_STRUCTURE_INVALID = "NetStream.Play.FileStructureInvalid"; - - /** - * This event is sent if the player does not detect any supported tracks. If there aren't any supported - * video, audio or data tracks found, Flash Player does not play the file. - */ - public static final String NS_PLAY_NO_SUPPORTED_TRACK_FOUND = "NetStream.Play.NoSupportedTrackFound"; - -} diff --git a/src/main/java/org/red5/server/net/rtmp/status/StatusObject.java b/src/main/java/org/red5/server/net/rtmp/status/StatusObject.java deleted file mode 100644 index 96c68ceef..000000000 --- a/src/main/java/org/red5/server/net/rtmp/status/StatusObject.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.status; - -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import org.red5.annotations.Anonymous; -import org.red5.io.object.ICustomSerializable; -import org.red5.io.object.Output; -import org.red5.io.object.Serializer; - -/** - * Status object that is sent to client with every status event - * @author The Red5 Project - * @author Luke Hubbard, Codegent Ltd (luke@codegent.com) - */ -@Anonymous -public class StatusObject implements Serializable, ICustomSerializable, Externalizable { - - private static final long serialVersionUID = 8817297676191096283L; - - public static final String ERROR = "error"; - - public static final String STATUS = "status"; - - public static final String WARNING = "warning"; - - protected String code; - - protected String level; - - protected String description = ""; - - protected Object application; - - protected Map additional; - - /** Constructs a new StatusObject. */ - public StatusObject() { - - } - - public StatusObject(String code, String level, String description) { - this.code = code; - this.level = level; - this.description = description; - } - - /** - * Getter for property 'code'. - * - * @return Value for property 'code'. - */ - public String getCode() { - return code; - } - - /** - * Setter for property 'code'. - * - * @param code Value to set for property 'code'. - */ - public void setCode(String code) { - this.code = code; - } - - /** - * Getter for property 'description'. - * - * @return Value for property 'description'. - */ - public String getDescription() { - return description; - } - - /** - * Setter for property 'description'. - * - * @param description Value to set for property 'description'. - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Getter for property 'level'. - * - * @return Value for property 'level'. - */ - public String getLevel() { - return level; - } - - /** - * Setter for property 'level'. - * - * @param level Value to set for property 'level'. - */ - public void setLevel(String level) { - this.level = level; - } - - /** - * Setter for property 'application'. - * - * @param application Value to set for property 'application'. - */ - public void setApplication(Object application) { - this.application = application; - } - - /** - * Getter for property 'application'. - * - * @return Value for property 'application'. - */ - public Object getApplication() { - return application; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return String.format("Status code: %s level: %s description: %s", code, level, description); - } - - /** - * Generate Status object that can be returned through a RTMP channel. - * - * @return status - */ - public Status asStatus() { - return new Status(getCode(), getLevel(), getDescription()); - } - - public void setAdditional(String name, Object value) { - if ("code,level,description,application".indexOf(name) != -1) { - throw new RuntimeException("the name \"" + name + "\" is reserved"); - } - if (additional == null) { - additional = new HashMap(); - } - additional.put(name, value); - } - - public void serialize(Output output) { - output.putString("level"); - output.writeString(getLevel()); - output.putString("code"); - output.writeString(getCode()); - output.putString("description"); - output.writeString(getDescription()); - if (application != null) { - output.putString("application"); - Serializer.serialize(output, application); - } - if (additional != null) { - // Add additional parameters - for (Map.Entry entry : additional.entrySet()) { - output.putString(entry.getKey()); - Serializer.serialize(output, entry.getValue()); - } - } - } - - @SuppressWarnings("unchecked") - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - code = (String) in.readObject(); - description = (String) in.readObject(); - level = (String) in.readObject(); - additional = (Map) in.readObject(); - application = in.readObject(); - } - - public void writeExternal(ObjectOutput out) throws IOException { - out.writeObject(code); - out.writeObject(description); - out.writeObject(level); - if (application != null) { - out.writeObject(additional); - } - if (additional != null) { - out.writeObject(application); - } - } -} diff --git a/src/main/java/org/red5/server/net/rtmp/status/StatusObjectService.java b/src/main/java/org/red5/server/net/rtmp/status/StatusObjectService.java deleted file mode 100644 index 31a83cba5..000000000 --- a/src/main/java/org/red5/server/net/rtmp/status/StatusObjectService.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmp.status; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.beanutils.BeanMap; -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.io.amf.Output; -import org.red5.io.object.Serializer; -import org.red5.io.utils.HexDump; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.InitializingBean; - -/** - * Service that works with status objects. - * Note all status object should aim to be under 128 bytes. - * - * @author The Red5 Project - * @author Luke Hubbard, Codegent Ltd (luke@codegent.com) - */ -public class StatusObjectService implements StatusCodes, InitializingBean { - - /** - * Logger - */ - protected static Logger log = LoggerFactory.getLogger(StatusObjectService.class); - - /** - * Status objects map - */ - protected Map statusObjects; - - /** - * Cached status objects map - */ - protected Map cachedStatusObjects; - - /** - * Initialization - */ - public void afterPropertiesSet() throws Exception { - log.trace("Loading status objects"); - loadStatusObjects(); - log.trace("Caching status objects"); - cacheStatusObjects(); - log.debug("Status service ready"); - } - - /** - * Creates all status objects and adds them to status objects map - */ - public void loadStatusObjects() { - statusObjects = new HashMap(); - - statusObjects.put(NC_CALL_FAILED, new StatusObject(NC_CALL_FAILED, StatusObject.ERROR, "")); - statusObjects.put(NC_CALL_BADVERSION, new StatusObject(NC_CALL_BADVERSION, StatusObject.ERROR, "")); - - statusObjects.put(NC_CONNECT_APPSHUTDOWN, new StatusObject(NC_CONNECT_APPSHUTDOWN, StatusObject.ERROR, "")); - statusObjects.put(NC_CONNECT_CLOSED, new StatusObject(NC_CONNECT_CLOSED, StatusObject.STATUS, "")); - statusObjects.put(NC_CONNECT_FAILED, new StatusObject(NC_CONNECT_FAILED, StatusObject.ERROR, "")); - statusObjects.put(NC_CONNECT_REJECTED, new StatusObject(NC_CONNECT_REJECTED, StatusObject.ERROR, "")); - statusObjects.put(NC_CONNECT_SUCCESS, new StatusObject(NC_CONNECT_SUCCESS, StatusObject.STATUS, "Connection succeeded.")); - statusObjects.put(NC_CONNECT_INVALID_APPLICATION, new StatusObject(NC_CONNECT_INVALID_APPLICATION, StatusObject.ERROR, "")); - - statusObjects.put(NS_INVALID_ARGUMENT, new StatusObject(NS_INVALID_ARGUMENT, StatusObject.ERROR, "")); - statusObjects.put(NS_CLEAR_SUCCESS, new StatusObject(NS_CLEAR_SUCCESS, StatusObject.STATUS, "")); - statusObjects.put(NS_CLEAR_FAILED, new StatusObject(NS_CLEAR_FAILED, StatusObject.ERROR, "")); - - statusObjects.put(NS_PUBLISH_START, new StatusObject(NS_PUBLISH_START, StatusObject.STATUS, "")); - statusObjects.put(NS_PUBLISH_BADNAME, new StatusObject(NS_PUBLISH_BADNAME, StatusObject.ERROR, "")); - statusObjects.put(NS_FAILED, new StatusObject(NS_FAILED, StatusObject.ERROR, "")); - statusObjects.put(NS_UNPUBLISHED_SUCCESS, new StatusObject(NS_UNPUBLISHED_SUCCESS, StatusObject.STATUS, "")); - - statusObjects.put(NS_RECORD_START, new StatusObject(NS_RECORD_START, StatusObject.STATUS, "")); - statusObjects.put(NS_RECORD_NOACCESS, new StatusObject(NS_RECORD_NOACCESS, StatusObject.ERROR, "")); - statusObjects.put(NS_RECORD_STOP, new StatusObject(NS_RECORD_STOP, StatusObject.STATUS, "")); - statusObjects.put(NS_RECORD_FAILED, new StatusObject(NS_RECORD_FAILED, StatusObject.ERROR, "")); - - statusObjects.put(NS_PLAY_INSUFFICIENT_BW, new RuntimeStatusObject(NS_PLAY_INSUFFICIENT_BW, StatusObject.WARNING, "")); - statusObjects.put(NS_PLAY_START, new RuntimeStatusObject(NS_PLAY_START, StatusObject.STATUS, "")); - statusObjects.put(NS_PLAY_STREAMNOTFOUND, new RuntimeStatusObject(NS_PLAY_STREAMNOTFOUND, StatusObject.ERROR, "")); - statusObjects.put(NS_PLAY_STOP, new RuntimeStatusObject(NS_PLAY_STOP, StatusObject.STATUS, "")); - statusObjects.put(NS_PLAY_FAILED, new RuntimeStatusObject(NS_PLAY_FAILED, StatusObject.ERROR, "")); - statusObjects.put(NS_PLAY_RESET, new RuntimeStatusObject(NS_PLAY_RESET, StatusObject.STATUS, "")); - statusObjects.put(NS_PLAY_PUBLISHNOTIFY, new RuntimeStatusObject(NS_PLAY_PUBLISHNOTIFY, StatusObject.STATUS, "")); - statusObjects.put(NS_PLAY_UNPUBLISHNOTIFY, new RuntimeStatusObject(NS_PLAY_UNPUBLISHNOTIFY, StatusObject.STATUS, "")); - - statusObjects.put(NS_DATA_START, new StatusObject(NS_DATA_START, StatusObject.STATUS, "")); - - statusObjects.put(APP_SCRIPT_ERROR, new StatusObject(APP_SCRIPT_ERROR, StatusObject.STATUS, "")); - statusObjects.put(APP_SCRIPT_WARNING, new StatusObject(APP_SCRIPT_WARNING, StatusObject.STATUS, "")); - statusObjects.put(APP_RESOURCE_LOWMEMORY, new StatusObject(APP_RESOURCE_LOWMEMORY, StatusObject.STATUS, "")); - statusObjects.put(APP_SHUTDOWN, new StatusObject(APP_SHUTDOWN, StatusObject.STATUS, "")); - statusObjects.put(APP_GC, new StatusObject(APP_GC, StatusObject.STATUS, "")); - - statusObjects.put(NS_PLAY_FILE_STRUCTURE_INVALID, new StatusObject(NS_PLAY_FILE_STRUCTURE_INVALID, StatusObject.ERROR, "")); - statusObjects.put(NS_PLAY_NO_SUPPORTED_TRACK_FOUND, new StatusObject(NS_PLAY_NO_SUPPORTED_TRACK_FOUND, StatusObject.ERROR, "")); - - } - - /** - * Cache status objects - */ - public void cacheStatusObjects() { - - cachedStatusObjects = new HashMap(); - - String statusCode; - IoBuffer out = IoBuffer.allocate(256); - out.setAutoExpand(true); - - for (String s : statusObjects.keySet()) { - statusCode = s; - StatusObject statusObject = statusObjects.get(statusCode); - if (statusObject instanceof RuntimeStatusObject) { - continue; - } - serializeStatusObject(out, statusObject); - out.flip(); - if (log.isTraceEnabled()) { - log.trace(HexDump.formatHexDump(out.getHexDump())); - } - byte[] cachedBytes = new byte[out.limit()]; - out.get(cachedBytes); - out.clear(); - cachedStatusObjects.put(statusCode, cachedBytes); - } - out.free(); - out = null; - } - - /** - * Serializes status object - * @param out Byte buffer for output object - * @param statusObject Status object to serialize - */ - public void serializeStatusObject(IoBuffer out, StatusObject statusObject) { - Map statusMap = new BeanMap(statusObject); - Output output = new Output(out); - Serializer.serialize(output, statusMap); - } - - /** - * Return status object by code - * @param statusCode Status object code - * @return Status object with given code - */ - public StatusObject getStatusObject(String statusCode) { - return statusObjects.get(statusCode); - } - - /** - * Return status object by code as byte array - * @param statusCode Status object code - * @return Status object with given code as byte array - */ - public byte[] getCachedStatusObjectAsByteArray(String statusCode) { - return cachedStatusObjects.get(statusCode); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmpe/RTMPEIoFilter.java b/src/main/java/org/red5/server/net/rtmpe/RTMPEIoFilter.java deleted file mode 100644 index b53eea53c..000000000 --- a/src/main/java/org/red5/server/net/rtmpe/RTMPEIoFilter.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.rtmpe; - -import javax.crypto.Cipher; - -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.filterchain.IoFilterAdapter; -import org.apache.mina.core.session.IoSession; -import org.apache.mina.core.write.WriteRequest; -import org.apache.mina.core.write.WriteRequestWrapper; -import org.red5.server.net.rtmp.RTMPConnManager; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.net.rtmp.RTMPHandshake; -import org.red5.server.net.rtmp.RTMPMinaConnection; -import org.red5.server.net.rtmp.codec.RTMP; -import org.red5.server.net.rtmp.message.Constants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RTMPE IO filter - * - * @author Peter Thomas (ptrthomas@gmail.com) - * @author Paul Gregoire (mondain@gmail.com) - */ -public class RTMPEIoFilter extends IoFilterAdapter { - - private static final Logger log = LoggerFactory.getLogger(RTMPEIoFilter.class); - - @Override - public void messageReceived(NextFilter nextFilter, IoSession session, Object obj) throws Exception { - String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); - log.trace("Session id: {}", sessionId); - RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); - RTMP rtmp = conn.getState(); - //if there is a handshake on the session, ensure the type has been set - if (session.containsAttribute(RTMPConnection.RTMP_HANDSHAKE)) { - log.trace("Handshake exists on the session"); - //get the handshake from the session - RTMPHandshake handshake = (RTMPHandshake) session.getAttribute(RTMPConnection.RTMP_HANDSHAKE); - int handshakeType = handshake.getHandshakeType(); - if (handshakeType == 0) { - log.trace("Handshake type is not currently set"); - // holds the handshake type, default is un-encrypted - byte handshakeByte = RTMPConnection.RTMP_NON_ENCRYPTED; - //get the current message - if (obj instanceof IoBuffer) { - IoBuffer message = (IoBuffer) obj; - message.mark(); - handshakeByte = message.get(); - message.reset(); - } - //set the type - handshake.setHandshakeType(handshakeByte); - //set on the rtmp state - rtmp.setEncrypted(handshakeByte == RTMPConnection.RTMP_ENCRYPTED ? true : false); - } else if (handshakeType == 3) { - if (rtmp.getState() == RTMP.STATE_CONNECTED) { - log.debug("In connected state"); - // remove handshake from session now that we are connected - session.removeAttribute(RTMPConnection.RTMP_HANDSHAKE); - log.debug("Using non-encrypted communications"); - } - } else if (handshakeType == 6) { - // ensure we have received enough bytes to be encrypted - long readBytesCount = conn.getReadBytes(); - long writeBytesCount = conn.getWrittenBytes(); - log.trace("Bytes read: {} written: {}", readBytesCount, writeBytesCount); - // don't remove the handshake when using RTMPE until we've written all the handshake data - if (writeBytesCount >= (Constants.HANDSHAKE_SIZE * 2)) { - //if we are connected and doing encryption, add the ciphers - log.debug("Assumed to be in a connected state"); - // remove handshake from session now that we are connected - session.removeAttribute(RTMPConnection.RTMP_HANDSHAKE); - log.debug("Using encrypted communications"); - //make sure they are not already on the session - if (session.containsAttribute(RTMPConnection.RTMPE_CIPHER_IN)) { - log.debug("Ciphers already exist on the session"); - } else { - log.debug("Adding ciphers to the session"); - session.setAttribute(RTMPConnection.RTMPE_CIPHER_IN, handshake.getCipherIn()); - session.setAttribute(RTMPConnection.RTMPE_CIPHER_OUT, handshake.getCipherOut()); - } - } - } - } - Cipher cipher = (Cipher) session.getAttribute(RTMPConnection.RTMPE_CIPHER_IN); - if (cipher != null) { //may want to verify handshake is complete as well - // assume message is an IoBuffer - IoBuffer message = (IoBuffer) obj; - if (rtmp.getState() == RTMP.STATE_HANDSHAKE) { - // ensure there are enough bytes to skip - if (message.limit() > Constants.HANDSHAKE_SIZE) { - //skip the first 1536 - byte[] handshakeReply = new byte[Constants.HANDSHAKE_SIZE]; - message.get(handshakeReply); - // TODO verify reply, for now just set to connected - rtmp.setState(RTMP.STATE_CONNECTED); - } else { - log.warn("There may be a network issue on this RTMPE connection: {}", conn); - return; - } - } - log.debug("Decrypting buffer: {}", message); - byte[] encrypted = new byte[message.remaining()]; - message.get(encrypted); - message.clear(); - message.free(); - byte[] plain = cipher.update(encrypted); - IoBuffer messageDecrypted = IoBuffer.wrap(plain); - log.debug("Decrypted buffer: {}", messageDecrypted); - nextFilter.messageReceived(session, messageDecrypted); - } else { - log.trace("Not decrypting message received: {}", obj); - nextFilter.messageReceived(session, obj); - } - } - - @Override - public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) throws Exception { - Cipher cipher = (Cipher) session.getAttribute(RTMPConnection.RTMPE_CIPHER_OUT); - if (cipher != null) { //may want to verify handshake is complete as well - IoBuffer message = (IoBuffer) request.getMessage(); - if (!message.hasRemaining()) { - // Ignore empty buffers - log.debug("Buffer was empty"); - } else { - log.debug("Encrypting buffer: {}", message); - byte[] plain = new byte[message.remaining()]; - message.get(plain); - message.clear(); - message.free(); - //encrypt and write - byte[] encrypted = cipher.update(plain); - IoBuffer messageEncrypted = IoBuffer.wrap(encrypted); - log.debug("Encrypted buffer: {}", messageEncrypted); - nextFilter.filterWrite(session, new EncryptedWriteRequest(request, messageEncrypted)); - } - } else { - log.trace("Not encrypting write request"); - nextFilter.filterWrite(session, request); - } - } - - private static class EncryptedWriteRequest extends WriteRequestWrapper { - private final IoBuffer encryptedMessage; - - private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) { - super(writeRequest); - this.encryptedMessage = encryptedMessage; - } - - @Override - public Object getMessage() { - return encryptedMessage; - } - } - -} diff --git a/src/main/java/org/red5/server/net/rtmpt/BaseRTMPTConnection.java b/src/main/java/org/red5/server/net/rtmpt/BaseRTMPTConnection.java deleted file mode 100644 index a91c77193..000000000 --- a/src/main/java/org/red5/server/net/rtmpt/BaseRTMPTConnection.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.commons.lang3.ArrayUtils; -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.Red5; -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; - -/** - * Base RTMPT client / session. - * - * @author The Red5 Project - * @author Paul Gregoire (mondain@gmail.com) - */ -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; - - /** - * 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 volatile IoBuffer buffer; - - /** - * List of pending outgoing messages - */ - protected volatile LinkedBlockingQueue pendingOutMessages = new LinkedBlockingQueue(); - - /** - * Maximum incoming messages to process at a time per client - */ - protected int maxInMessagesPerProcess = 16; - - /** - * Maximum amount of time in milliseconds to wait before allowing an offer to fail - */ - protected long maxQueueOfferTime = 500L; - - /** - * Maximum offer attempts before failing on incoming or outgoing queues - */ - protected int maxQueueOfferAttempts = 4; - - public BaseRTMPTConnection(String type) { - super(type); - this.buffer = IoBuffer.allocate(0).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() { - closing = true; - if (pendingOutMessages.size() > 0) { - log.trace("Clearing pending messages out: {}", pendingOutMessages.size()); - pendingOutMessages.clear(); - } - // clean up buffer - if (buffer != null) { - buffer.free(); - buffer = null; - } - super.close(); - } - - /** - * Getter for property 'closing'. - * - * @return Value for property 'closing'. - */ - public boolean isClosing() { - return closing; - } - - /** {@inheritDoc} */ - @Override - public long getReadBytes() { - return readBytes.get(); - } - - public void updateReadBytes(int read) { - readBytes.addAndGet(read); - } - - /** {@inheritDoc} */ - @Override - public long getWrittenBytes() { - return writtenBytes.get(); - } - - public void updateWrittenBytes(int wrote) { - writtenBytes.addAndGet(wrote); - } - - /** {@inheritDoc} */ - @Override - public long getPendingMessages() { - log.debug("Checking pending queue size. Session id: {} closing: {} state: {}", sessionId, closing, state); - if (state.getState() == RTMP.STATE_DISCONNECTED) { - log.debug("Connection is disconnected"); - pendingOutMessages.clear(); - } - return pendingOutMessages.size(); - } - - /** - * 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"); - if (closing || state.getState() == RTMP.STATE_DISCONNECTED) { - // connection is being closed, don't decode any new packets - return Collections.EMPTY_LIST; - } - log.trace("Current bytes read at decode: {}", 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 - packet: {}", packet); - //log.trace("state: {}", state); - if (closing || state.getState() == RTMP.STATE_DISCONNECTED) { - // connection is being closed, don't send any new packets - log.debug("No write completed due to connection disconnecting"); - } else { - IoBuffer data = null; - try { - // set the connection local before attempting to encode - log.debug("Local: {} this: {}", Red5.getConnectionLocal(), this); - Red5.setConnectionLocal(this); - // encode the data - data = encoder.encodePacket(packet); - if (data != null) { - // add to pending - log.debug("Adding outgoing message packet"); - PendingData pendingData = new PendingData(data, packet); - try { - int attempt = 0; - while (!pendingOutMessages.offer(pendingData, maxQueueOfferTime, TimeUnit.MILLISECONDS)) { - log.trace("Packet was not added to out queue"); - attempt++; - if (attempt >= maxQueueOfferAttempts) { - break; - } - } - } catch (InterruptedException ex) { - log.warn("Offering packet to out queue failed", ex); - } - } else { - log.warn("Response buffer was null after encoding"); - } - } catch (Exception e) { - log.error("Could not encode message {}", packet, e); - } - } - } - - /** - * Send raw data down the connection. - * - * @param packet the buffer containing the raw data - */ - @Override - public void writeRaw(IoBuffer packet) { - log.debug("write - io buffer: {}", packet); - PendingData pendingData = new PendingData(packet); - try { - int attempt = 0; - while (!pendingOutMessages.offer(pendingData, maxQueueOfferTime, TimeUnit.MILLISECONDS)) { - log.trace("Packet was not added to out queue"); - attempt++; - if (attempt >= maxQueueOfferAttempts) { - break; - } - } - } catch (InterruptedException ex) { - log.warn("Offering io buffer to out queue failed", ex); - } - } - - protected IoBuffer foldPendingMessages(int targetSize) { - log.debug("foldPendingMessages - target size: {}", targetSize); - IoBuffer result = null; - if (!pendingOutMessages.isEmpty()) { - int available = pendingOutMessages.size(); - // create list to hold outgoing data - LinkedList sendList = new LinkedList(); - pendingOutMessages.drainTo(sendList, Math.min(164, available)); - result = IoBuffer.allocate(targetSize).setAutoExpand(true); - for (PendingData pendingMessage : sendList) { - result.put(pendingMessage.getBuffer()); - Packet packet = pendingMessage.getPacket(); - if (packet != null) { - try { - handler.messageSent(this, packet); - // mark packet as being written - writingMessage(packet); - } catch (Exception e) { - log.error("Could not notify stream subsystem about sent message", e); - } - } else { - log.trace("Pending message did not have a packet"); - } - } - sendList.clear(); - result.flip(); - // send byte length - log.debug("Send size: {}", result.limit()); - } - return result; - } - - public void setDecoder(RTMPProtocolDecoder decoder) { - this.decoder = (RTMPTProtocolDecoder) decoder; - } - - public void setEncoder(RTMPProtocolEncoder encoder) { - this.encoder = (RTMPTProtocolEncoder) encoder; - } - - /** - * @param maxInMessagesPerProcess the maxInMessagesPerProcess to set - */ - public void setMaxInMessagesPerProcess(int maxInMessagesPerProcess) { - this.maxInMessagesPerProcess = maxInMessagesPerProcess; - } - - /** - * @param maxQueueOfferTime the maxQueueOfferTime to set - */ - public void setMaxQueueOfferTime(long maxQueueOfferTime) { - this.maxQueueOfferTime = maxQueueOfferTime; - } - - /** - * @param maxQueueOfferAttempts the maxQueueOfferAttempts to set - */ - public void setMaxQueueOfferAttempts(int maxQueueOfferAttempts) { - this.maxQueueOfferAttempts = maxQueueOfferAttempts; - } - - /** - * Holder for data destined for a requester that is not ready to be sent. - */ - private static class PendingData { - - // simple packet - private final Packet packet; - - // encoded packet data - private final byte[] byteBuffer; - - private PendingData(IoBuffer buffer, Packet packet) { - int size = buffer.limit(); - this.byteBuffer = new byte[size]; - buffer.get(byteBuffer); - this.packet = packet; - if (log.isTraceEnabled()) { - log.trace("Buffer: {}", Arrays.toString(ArrayUtils.subarray(byteBuffer, 0, 32))); - } - } - - private PendingData(IoBuffer buffer) { - int size = buffer.limit(); - this.byteBuffer = new byte[size]; - buffer.get(byteBuffer); - this.packet = null; - if (log.isTraceEnabled()) { - log.trace("Buffer: {}", Arrays.toString(ArrayUtils.subarray(byteBuffer, 0, 32))); - } - } - - public byte[] getBuffer() { - if (log.isTraceEnabled()) { - log.trace("Get buffer: {}", Arrays.toString(ArrayUtils.subarray(byteBuffer, 0, 32))); - } - return byteBuffer; - } - - public Packet getPacket() { - return packet; - } - - @SuppressWarnings("unused") - public int getBufferSize() { - if (byteBuffer != null) { - return byteBuffer.length; - } - return 0; - } - - } - -} diff --git a/src/main/java/org/red5/server/net/rtmpt/RTMPTConnection.java b/src/main/java/org/red5/server/net/rtmpt/RTMPTConnection.java deleted file mode 100644 index 94ad00696..000000000 --- a/src/main/java/org/red5/server/net/rtmpt/RTMPTConnection.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.InboundHandshake; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.net.rtmp.RTMPHandshake; -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; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; - -/** - * A RTMPT client / session. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * @author Paul Gregoire (mondain@gmail.com) - */ -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 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 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 - */ - protected IoSession getSession() { - return ioSession; - } - - /** {@inheritDoc} */ - @Override - protected void onInactive() { - close(); - } - - /** {@inheritDoc} */ - @Override - public void close() { - log.debug("close {} state: {}", getSessionId(), state.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) { - } - } - // 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 { - handleMessageReceived((IoBuffer) message); - } - } - - /** - * Handle raw buffer receiving event. - * - * @param message - * Data buffer - */ - public void handleMessageReceived(IoBuffer message) { - log.trace("handleMessageReceived (raw buffer) - {}", sessionId); - if (getStateCode() != RTMP.STATE_HANDSHAKE) { - log.warn("Raw buffer after handshake, something odd going on"); - } - log.debug("Writing handshake reply, handskake size: {}", message.remaining()); - RTMPHandshake shake = new InboundHandshake(); - shake.setHandshakeType(RTMPConnection.RTMP_NON_ENCRYPTED); - writeRaw(shake.doHandshake(message)); - // clean up - ((IoBuffer) message).free(); - message = null; - } - - /** {@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; - } - - /** {@inheritDoc} */ - @Override - public void setScheduler(ThreadPoolTaskScheduler scheduler) { - super.setScheduler(scheduler); - } - - /** - * Set the servlet that created the connection. - * - * @param 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() { - log.trace("getPollingDelay {}", pollingDelay); - log.trace("Polling delay: {} loops without messages: {}", pollingDelay, noPendingMessages); - return (byte) (pollingDelay + 1); - } - - /** - * {@inheritDoc} - */ - @Override - public IoBuffer getPendingMessages(int targetSize) { - log.debug("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 - * */ - public Long getLastDataReceived() { - return tsLastDataReceived; - } - -} diff --git a/src/main/java/org/red5/server/net/rtmpt/RTMPTHandler.java b/src/main/java/org/red5/server/net/rtmpt/RTMPTHandler.java deleted file mode 100644 index f51b077b9..000000000 --- a/src/main/java/org/red5/server/net/rtmpt/RTMPTHandler.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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 org.red5.server.net.rtmp.RTMPHandler; -import org.red5.server.net.rtmpt.codec.RTMPTCodecFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Handler for RTMPT messages. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * @author Paul Gregoire (mondain@gmail.com) - */ -public class RTMPTHandler extends RTMPHandler { - - private static final Logger log = LoggerFactory.getLogger(RTMPTHandler.class); - - /** - * Protocol codec factory - */ - protected RTMPTCodecFactory codecFactory; - - /** - * Setter for codec factory - * - * @param factory Codec factory to use - */ - public void setCodecFactory(RTMPTCodecFactory factory) { - this.codecFactory = factory; - } - - /** - * Getter for codec factory - * - * @return Codec factory - */ - public RTMPTCodecFactory getCodecFactory() { - return this.codecFactory; - } - - /** - * Return hostname for URL. - * - * @param url - * URL - * @return Hostname from that URL - */ - @Override - protected String getHostname(String url) { - log.debug("url: {}", url); - String[] parts = url.split("/"); - if (parts.length == 2) { - return ""; - } else { - String host = parts[2]; - // strip out default port if the client added it - if (host.endsWith(":80")) { - // Remove default port from connection string - return host.substring(0, host.length() - 3); - } - return host; - } - } - -} diff --git a/src/main/java/org/red5/server/net/rtmpt/RTMPTServlet.java b/src/main/java/org/red5/server/net/rtmpt/RTMPTServlet.java deleted file mode 100644 index af8415325..000000000 --- a/src/main/java/org/red5/server/net/rtmpt/RTMPTServlet.java +++ /dev/null @@ -1,651 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import javax.servlet.ServletContext; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.logging.Red5LoggerFactory; -import org.red5.server.api.Red5; -import org.red5.server.net.IConnectionManager; -import org.red5.server.net.rtmp.RTMPConnManager; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.net.rtmp.codec.RTMPProtocolEncoder; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.net.rtmp.message.Packet; -import org.red5.server.net.rtmp.status.Status; -import org.red5.server.net.rtmp.status.StatusCodes; -import org.red5.server.net.servlet.ServletUtils; -import org.red5.server.service.PendingCall; -import org.slf4j.Logger; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.support.WebApplicationContextUtils; - - -/** - * Servlet that handles all RTMPT requests. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * @author Paul Gregoire (mondain@gmail.com) - */ -public class RTMPTServlet extends HttpServlet { - - private static final long serialVersionUID = 5925399677454936613L; - - protected static Logger log = Red5LoggerFactory.getLogger(RTMPTServlet.class); - - /** - * HTTP request method to use for RTMPT calls. - */ - private static final String REQUEST_METHOD = "POST"; - - /** - * Content-Type to use for RTMPT requests / responses. - */ - private static final String CONTENT_TYPE = "application/x-fcs"; - - /** - * Connection manager. - */ - private static IConnectionManager manager; - - /** - * Try to generate responses that contain at least 32768 bytes data. - * Increasing this value results in better stream performance, but also increases the latency. - */ - private static int targetResponseSize = Short.MAX_VALUE + 1; - - /** - * Reference to RTMPT handler; - */ - private static RTMPTHandler handler; - - /** - * Response sent for ident2 requests. If this is null a 404 will be returned - */ - private static String ident2; - - // Whether or not to enforce content type checking for requests - private boolean enforceContentTypeCheck; - - /** - * Thread local for request info storage - */ - protected ThreadLocal requestInfo = new ThreadLocal(); - - /** - * Web app context - */ - protected transient WebApplicationContext applicationContext; - - /** - * Return an error message to the client. - * - * @param message Message - * @param resp Servlet response - * @throws IOException - */ - protected void handleBadRequest(String message, HttpServletResponse resp) throws IOException { - log.debug("handleBadRequest {}", message); -// resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); -// resp.setHeader("Connection", "Keep-Alive"); -// resp.setHeader("Cache-Control", "no-cache"); -// resp.setContentType("text/plain"); -// resp.setContentLength(message.length()); -// resp.getWriter().write(message); -// resp.flushBuffer(); - - // create and send a rejected status - Status status = new Status(StatusCodes.NC_CONNECT_REJECTED, Status.ERROR, message); - PendingCall call = new PendingCall(null, "onStatus", new Object[] { status }); - Invoke event = new Invoke(); - event.setCall(call); - Header header = new Header(); - Packet packet = new Packet(header, event); - header.setDataType(event.getDataType()); - // create dummy connection if local is empty - RTMPConnection conn = (RTMPConnection) Red5.getConnectionLocal(); - if (conn == null) { - try { - conn = ((RTMPConnManager) manager).createConnectionInstance(RTMPTConnection.class); - Red5.setConnectionLocal(conn); - } catch (Exception e) { - } - } - // encode the data - RTMPProtocolEncoder encoder = new RTMPProtocolEncoder(); - IoBuffer out = encoder.encodePacket(packet); - // send the response - returnMessage(null, out, resp); - // clear local - Red5.setConnectionLocal(null); - } - - /** - * Return a single byte to the client. - * - * @param message Message - * @param resp Servlet response - * @throws IOException I/O exception - */ - protected void returnMessage(byte message, HttpServletResponse resp) throws IOException { - log.debug("returnMessage {}", message); - resp.setStatus(HttpServletResponse.SC_OK); - resp.setHeader("Connection", "Keep-Alive"); - resp.setHeader("Cache-Control", "no-cache"); - resp.setContentType(CONTENT_TYPE); - resp.setContentLength(1); - resp.getWriter().write(message); - resp.flushBuffer(); - } - - /** - * Return a message to the client. - * - * @param message Message - * @param resp Servlet response - * @throws IOException I/O exception - */ - protected void returnMessage(String message, HttpServletResponse resp) throws IOException { - log.debug("returnMessage {}", message); - resp.setStatus(HttpServletResponse.SC_OK); - resp.setHeader("Connection", "Keep-Alive"); - resp.setHeader("Cache-Control", "no-cache"); - resp.setContentType(CONTENT_TYPE); - resp.setContentLength(message.length()); - resp.getWriter().write(message); - resp.flushBuffer(); - } - - /** - * Return raw data to the client. - * - * @param conn RTMP connection - * @param buffer Raw data as byte buffer - * @param resp Servlet response - * @throws IOException I/O exception - */ - protected void returnMessage(RTMPTConnection conn, IoBuffer buffer, HttpServletResponse resp) throws IOException { - log.trace("returnMessage {}", buffer); - if (conn != null) { - resp.setStatus(HttpServletResponse.SC_OK); - } else { - resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); - } - resp.setHeader("Connection", "Keep-Alive"); - resp.setHeader("Cache-Control", "no-cache"); - resp.setContentType(CONTENT_TYPE); - int contentLength = buffer.limit() + 1; - resp.setContentLength(contentLength); - ServletOutputStream output = resp.getOutputStream(); - if (conn != null) { - byte pollingDelay = conn.getPollingDelay(); - log.debug("Sending {} bytes; polling delay: {}", buffer.limit(), pollingDelay); - output.write(pollingDelay); - } else { - output.write((byte) 0); - } - ServletUtils.copy(buffer.asInputStream(), output); - if (conn != null) { - conn.updateWrittenBytes(contentLength); - } - buffer.free(); - buffer = null; - } - - /** - * Sets the request info for the current request. Request info contains the session id and request number gathered from - * the incoming request. The URI is in this form /[method]/[session id]/[request number] ie. /send/CAFEBEEF01/7 - * - * @param req Servlet request - */ - protected void setRequestInfo(HttpServletRequest req) { - String[] arr = req.getRequestURI().trim().split("/"); - log.trace("Request parts: {}", Arrays.toString(arr)); - RequestInfo info = new RequestInfo(arr[2], Integer.valueOf(arr[3])); - requestInfo.set(info); - } - - /** - * Skip data sent by the client. - * - * @param req - * Servlet request - * @throws IOException - * I/O exception - */ - protected void skipData(HttpServletRequest req) throws IOException { - log.trace("skipData {}", req); - int length = req.getContentLength(); - log.trace("Skipping {} bytes", length); - IoBuffer data = IoBuffer.allocate(length); - ServletUtils.copy(req, data.asOutputStream()); - data.flip(); - data.free(); - data = null; - log.trace("Skipped {} bytes", length); - } - - /** - * Send pending messages to client. - * - * @param conn RTMP connection - * @param resp Servlet response - */ - protected void returnPendingMessages(RTMPTConnection conn, HttpServletResponse resp) { - log.debug("returnPendingMessages {}", conn); - // grab any pending outgoing data - IoBuffer data = conn.getPendingMessages(targetResponseSize); - if (data != null) { - try { - returnMessage(conn, data, resp); - } catch (Exception ex) { - // using "Exception" is meant to catch any exception that would occur when doing a write - // this can be an IOException or a container specific one like ClientAbortException from catalina - log.warn("Exception returning outgoing data", ex); - conn.close(); - } - } else { - log.debug("No messages to send"); - if (conn.isClosing()) { - log.debug("Client is closing, send close notification"); - try { - // tell client to close connection - returnMessage((byte) 0, resp); - } catch (IOException ex) { - log.warn("Exception returning outgoing data - close notification", ex); - } - } else { - try { - returnMessage(conn.getPollingDelay(), resp); - } catch (IOException ex) { - log.warn("Exception returning outgoing data - polling delay", ex); - } - } - } - } - - /** - * Start a new RTMPT session. - * - * @param req Servlet request - * @param resp Servlet response - * @throws IOException I/O exception - */ - protected void handleOpen(HttpServletRequest req, HttpServletResponse resp) throws IOException { - log.debug("handleOpen"); - // skip sent data - skipData(req); - // TODO: should we evaluate the pathinfo? - RTMPTConnection conn = (RTMPTConnection) manager.createConnection(RTMPTConnection.class); - log.trace("{}", conn); - if (conn != null) { - // set properties - conn.setServlet(this); - conn.setServletRequest(req); - // add the connection to the manager - manager.setConnection(conn); - // set handler - conn.setHandler(handler); - conn.setDecoder(handler.getCodecFactory().getRTMPDecoder()); - conn.setEncoder(handler.getCodecFactory().getRTMPEncoder()); - handler.connectionOpened(conn); - conn.dataReceived(); - conn.updateReadBytes(req.getContentLength()); - // set thread local reference - Red5.setConnectionLocal(conn); - if (conn.getId() != 0) { - // return session id to client - returnMessage(conn.getSessionId() + "\n", resp); - } else { - // no more clients are available for serving - returnMessage((byte) 0, resp); - } - } else { - resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - resp.setHeader("Connection", "Keep-Alive"); - resp.setHeader("Cache-Control", "no-cache"); - resp.flushBuffer(); - } - } - - /** - * Close a RTMPT session. - * - * @param req Servlet request - * @param resp Servlet response - * @throws IOException I/O exception - */ - protected void handleClose(HttpServletRequest req, HttpServletResponse resp) throws IOException { - log.debug("handleClose"); - // skip sent data - skipData(req); - // get the associated connection - RTMPTConnection connection = getConnection(); - if (connection != null) { - log.debug("Pending messges on close: {}", connection.getPendingMessages()); - returnMessage((byte) 0, resp); - connection.close(); - } else { - handleBadRequest(String.format("Close: unknown client session: %s", requestInfo.get().getSessionId()), resp); - } - } - - /** - * Add data for an established session. - * - * @param req Servlet request - * @param resp Servlet response - * @throws IOException I/O exception - */ - protected void handleSend(HttpServletRequest req, HttpServletResponse resp) throws IOException { - log.debug("handleSend"); - final RTMPTConnection conn = getConnection(); - if (conn != null) { - // put the received data in a ByteBuffer - int length = req.getContentLength(); - log.trace("Request content length: {}", length); - final IoBuffer data = IoBuffer.allocate(length); - ServletUtils.copy(req, data.asOutputStream()); - data.flip(); - // decode the objects in the data - final List messages = conn.decode(data); - // clear the buffer - data.free(); - // messages are either of IoBuffer or Packet type - // handshaking uses IoBuffer and everything else should be Packet - for (Object message : messages) { - conn.handleMessageReceived(message); - } - conn.dataReceived(); - conn.updateReadBytes(length); - // return pending messages - returnPendingMessages(conn, resp); - } else { - handleBadRequest(String.format("Send: unknown client session: %s", requestInfo.get().getSessionId()), resp); - } - } - - /** - * Poll RTMPT session for updates. - * - * @param req Servlet request - * @param resp Servlet response - * @throws IOException I/O exception - */ - protected void handleIdle(HttpServletRequest req, HttpServletResponse resp) throws IOException { - log.debug("handleIdle"); - // skip sent data - skipData(req); - // get associated connection - RTMPTConnection conn = getConnection(); - if (conn != null) { - conn.dataReceived(); - conn.updateReadBytes(req.getContentLength()); - // return pending - returnPendingMessages(conn, resp); - } else { - handleBadRequest(String.format("Idle: unknown client session: %s", requestInfo.get().getSessionId()), resp); - } - } - - /** - * Main entry point for the servlet. - * - * @param req Request object - * @param resp Response object - * @throws IOException I/O exception - */ - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { - if (applicationContext == null) { - ServletContext ctx = getServletContext(); - applicationContext = WebApplicationContextUtils.getWebApplicationContext(ctx); - if (applicationContext == null) { - applicationContext = (WebApplicationContext) ctx.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); - } - log.debug("Application context: {}", applicationContext); - // ensure we have a connection manager - if (manager == null) { - log.warn("Class instance connection manager was null, looking up in application context"); - manager = (RTMPConnManager) applicationContext.getBean("rtmpConnManager"); - if (manager == null) { - log.warn("Connection manager was null in context, getting class instance"); - manager = RTMPConnManager.getInstance(); - if (manager == null) { - log.error("Connection manager is still null, this is bad"); - } - } - } - } - log.debug("Request - method: {} content type: {} path: {}", new Object[] { req.getMethod(), req.getContentType(), req.getServletPath() }); - // allow only POST requests with valid content length - if (!REQUEST_METHOD.equals(req.getMethod()) || req.getContentLength() == 0) { - // Bad request - return simple error page - handleBadRequest("Bad request, only RTMPT supported.", resp); - return; - } - // decide whether or not to enforce request content checks - if (enforceContentTypeCheck && !CONTENT_TYPE.equals(req.getContentType())) { - handleBadRequest(String.format("Bad request, unsupported content type: %s.", req.getContentType()), resp); - return; - } - // get the uri - String uri = req.getRequestURI().trim(); - log.debug("URI: {}", uri); - // get the path - String path = req.getServletPath(); - // since the only current difference in the type of request that we are interested in is the 'second' character, we can double - // the speed of this entry point by using a switch on the second character. - char p = path.charAt(1); - switch (p) { - case 'o': // OPEN_REQUEST - handleOpen(req, resp); - break; - case 'c': // CLOSE_REQUEST - setRequestInfo(req); - handleClose(req, resp); - requestInfo.remove(); - break; - case 's': // SEND_REQUEST - setRequestInfo(req); - handleSend(req, resp); - requestInfo.remove(); - break; - case 'i': // IDLE_REQUEST - setRequestInfo(req); - handleIdle(req, resp); - requestInfo.remove(); - break; - case 'f': // HTTPIdent request (ident and ident2) - //if HTTPIdent is requested send back some Red5 info - //http://livedocs.adobe.com/flashmediaserver/3.0/docs/help.html?content=08_xmlref_011.html - String ident = "Red5Red5 Server"; - // handle ident2 slightly different to appease osx clients - if (uri.charAt(uri.length() - 1) == '2') { - // check for pre-configured ident2 value - if (ident2 != null) { - ident = ident2; - } else { - // just send 404 back if no ident2 value is set - resp.setStatus(HttpServletResponse.SC_NOT_FOUND); - resp.setHeader("Connection", "Keep-Alive"); - resp.setHeader("Cache-Control", "no-cache"); - resp.flushBuffer(); - break; - } - } - resp.setStatus(HttpServletResponse.SC_OK); - resp.setHeader("Connection", "Keep-Alive"); - resp.setHeader("Cache-Control", "no-cache"); - resp.setContentType(CONTENT_TYPE); - resp.setContentLength(ident.length()); - resp.getWriter().write(ident); - resp.flushBuffer(); - break; - default: - handleBadRequest(String.format("RTMPT command %s is not supported.", path), resp); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - - /** {@inheritDoc} */ - @Override - public void destroy() { - // Cleanup connections - Collection conns = manager.getAllConnections(); - for (RTMPConnection conn : conns) { - if (conn instanceof RTMPTConnection) { - log.debug("Connection scope on destroy: {}", conn.getScope()); - conn.close(); - } - } - super.destroy(); - } - - /** - * Returns a connection based on the current client session id. - * - * @return RTMPTConnection - */ - protected RTMPTConnection getConnection() { - String sessionId = requestInfo.get().getSessionId(); - RTMPTConnection conn = (RTMPTConnection) manager.getConnectionBySessionId(sessionId); - if (conn != null) { - // check for non-connected state - if (!conn.isDisconnected()) { - // clear thread local reference - Red5.setConnectionLocal(conn); - } else { - removeConnection(sessionId); - } - } else { - log.warn("Null connection for session id: {}", sessionId); - } - return conn; - } - - /** - * Removes a connection matching the given session id from the connection manager. - * - * @param sessionId - */ - protected void removeConnection(String sessionId) { - log.debug("Removing connection for session id: {}", sessionId); - RTMPTConnection conn = (RTMPTConnection) manager.getConnectionBySessionId(sessionId); - if (conn != null) { - manager.removeConnection(conn.getSessionId()); - } else { - log.warn("Remove failed, null connection for session id: {}", sessionId); - } - } - - /** - * @param manager the manager to set - */ - public void setManager(IConnectionManager manager) { - log.trace("Set connection manager: {}", manager); - RTMPTServlet.manager = manager; - } - - /** - * Set the RTMPTHandler to use in this servlet. - * - * @param handler handler - */ - public void setHandler(RTMPTHandler handler) { - log.trace("Set handler: {}", handler); - RTMPTServlet.handler = handler; - } - - /** - * Set the fcs/ident2 string - * - * @param ident2 - */ - public void setIdent2(String ident2) { - RTMPTServlet.ident2 = ident2; - } - - /** - * Sets the target size for responses - * - * @param targetResponseSize the targetResponseSize to set - */ - public void setTargetResponseSize(int targetResponseSize) { - RTMPTServlet.targetResponseSize = targetResponseSize; - } - - /** - * @return the enforceContentTypeCheck - */ - public boolean isEnforceContentTypeCheck() { - return enforceContentTypeCheck; - } - - /** - * @param enforceContentTypeCheck the enforceContentTypeCheck to set - */ - public void setEnforceContentTypeCheck(boolean enforceContentTypeCheck) { - this.enforceContentTypeCheck = enforceContentTypeCheck; - } - - /** - * Used to store request information per thread. - */ - protected final class RequestInfo { - - private String sessionId; - - private Integer requestNumber; - - RequestInfo(String sessionId, Integer requestNumber) { - this.sessionId = sessionId; - this.requestNumber = requestNumber; - } - - /** - * @return the sessionId - */ - public String getSessionId() { - return sessionId; - } - - /** - * @return the requestNumber - */ - public Integer getRequestNumber() { - return requestNumber; - } - - } - -} diff --git a/src/main/java/org/red5/server/net/rtmpt/codec/RTMPTCodecFactory.java b/src/main/java/org/red5/server/net/rtmpt/codec/RTMPTCodecFactory.java deleted file mode 100644 index ad35388c6..000000000 --- a/src/main/java/org/red5/server/net/rtmpt/codec/RTMPTCodecFactory.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.codec; - -import org.red5.server.net.rtmp.codec.RTMPCodecFactory; -import org.red5.server.net.rtmp.codec.RTMPProtocolDecoder; -import org.red5.server.net.rtmp.codec.RTMPProtocolEncoder; - -/** - * RTMPT codec factory creates RTMP codec objects - */ -public class RTMPTCodecFactory extends RTMPCodecFactory { - - /** - * RTMP decoder - */ - private RTMPTProtocolDecoder decoder; - - /** - * RTMP encoder - */ - private ThreadLocal encoder; - - private long baseTolerance = 5000; - - private boolean dropLiveFuture; - - /** - * Initialization - */ - public void init() { - // decoder is ok for sharing between rtmpt connections - decoder = new RTMPTProtocolDecoder(); - encoder = new ThreadLocal() { - protected RTMPTProtocolEncoder initialValue() { - RTMPTProtocolEncoder enc = new RTMPTProtocolEncoder(); - enc.setBaseTolerance(baseTolerance); - enc.setDropLiveFuture(dropLiveFuture); - return enc; - } - }; - } - - /** - * @param baseTolerance the baseTolerance to set - */ - public void setBaseTolerance(long baseTolerance) { - this.baseTolerance = baseTolerance; - } - - /** - * @param dropLiveFuture the dropLiveFuture to set - */ - public void setDropLiveFuture(boolean dropLiveFuture) { - this.dropLiveFuture = dropLiveFuture; - } - - /** {@inheritDoc} */ - @Override - public RTMPProtocolDecoder getRTMPDecoder() { - return decoder; - } - - /** {@inheritDoc} */ - @Override - public RTMPProtocolEncoder getRTMPEncoder() { - return encoder.get(); - } - -} diff --git a/src/main/java/org/red5/server/net/rtmpt/codec/RTMPTProtocolDecoder.java b/src/main/java/org/red5/server/net/rtmpt/codec/RTMPTProtocolDecoder.java deleted file mode 100644 index 5a2b261db..000000000 --- a/src/main/java/org/red5/server/net/rtmpt/codec/RTMPTProtocolDecoder.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.codec; - -import org.red5.server.net.rtmp.codec.RTMPProtocolDecoder; - -/** - * RTMPT protocol decoder. To be implemeted. - */ -public class RTMPTProtocolDecoder extends RTMPProtocolDecoder { - - /* - * Nothing special here right now... - */ -} diff --git a/src/main/java/org/red5/server/net/rtmpt/codec/RTMPTProtocolEncoder.java b/src/main/java/org/red5/server/net/rtmpt/codec/RTMPTProtocolEncoder.java deleted file mode 100644 index 69305cc4b..000000000 --- a/src/main/java/org/red5/server/net/rtmpt/codec/RTMPTProtocolEncoder.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.codec; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.service.IServiceCall; -import org.red5.server.net.ICommand; -import org.red5.server.net.rtmp.codec.RTMPProtocolEncoder; -import org.red5.server.net.rtmp.status.Status; - -/** - * RTMPT protocol encoder. - */ -public class RTMPTProtocolEncoder extends RTMPProtocolEncoder { - - @Override - protected void encodeCommand(IoBuffer out, ICommand command) { - // if we get an InsufficientBW message for the client, we'll reduce the base tolerance and set drop live to true - final IServiceCall call = command.getCall(); - if ("onStatus".equals(call.getServiceMethodName()) && call.getArguments().length >= 1) { - Object arg0 = call.getArguments()[0]; - if ("NetStream.Play.InsufficientBW".equals(((Status) arg0).getCode())) { - long baseT = getBaseTolerance(); - try { - // drop the tolerances by half but not less than 500 - setBaseTolerance(Math.max(baseT / 2, 500)); - } catch (Exception e) { - log.debug("Problem setting base tolerance: {}", e.getMessage()); - } - setDropLiveFuture(true); - } - } - super.encodeCommand(out, command); - } - -} diff --git a/src/main/java/org/red5/server/net/servlet/ServletUtils.java b/src/main/java/org/red5/server/net/servlet/ServletUtils.java deleted file mode 100644 index 029566581..000000000 --- a/src/main/java/org/red5/server/net/servlet/ServletUtils.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.servlet; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.servlet.http.HttpServletRequest; - -import org.red5.logging.Red5LoggerFactory; -import org.slf4j.Logger; - -public class ServletUtils { - - private static Logger log = Red5LoggerFactory.getLogger(ServletUtils.class); - - /** - * Default value is 2048. - */ - public static final int DEFAULT_BUFFER_SIZE = 2048; - - /** - * Copies information from the input stream to the output stream using a - * default buffer size of 2048 bytes. - * @param input input - * @param output output - * - * @throws java.io.IOException on error - */ - public static void copy(InputStream input, OutputStream output) throws IOException { - copy(input, output, DEFAULT_BUFFER_SIZE); - } - - /** - * Copies information from the input stream to the output stream using the - * specified buffer size - * - * @param input input - * @param bufferSize buffer size - * @param output output - * @throws java.io.IOException on error - */ - public static void copy(InputStream input, OutputStream output, int bufferSize) throws IOException { - int availableBytes = input.available(); - log.debug("copy - bufferSize: {} available: {}", bufferSize, availableBytes); - byte[] buf = null; - if (availableBytes > 0) { - if (availableBytes >= bufferSize) { - buf = new byte[bufferSize]; - } else { - buf = new byte[availableBytes]; - } - int bytesRead = input.read(buf); - while (bytesRead != -1) { - output.write(buf, 0, bytesRead); - bytesRead = input.read(buf); - log.trace("Bytes read: {}", bytesRead); - } - output.flush(); - } else { - log.debug("Available is 0, attempting to read anyway"); - buf = new byte[bufferSize]; - int bytesRead = input.read(buf); - while (bytesRead != -1) { - output.write(buf, 0, bytesRead); - bytesRead = input.read(buf); - log.trace("Bytes read: {}", bytesRead); - } - output.flush(); - } - } - - /** - * Copies information from the http request to the output stream using the specified content length. - * - * @param req Request - * @param output Output stream - * @throws java.io.IOException on error - */ - public static void copy(HttpServletRequest req, OutputStream output) throws IOException { - InputStream input = req.getInputStream(); - int availableBytes = req.getContentLength(); - log.debug("copy - available: {}", availableBytes); - if (availableBytes > 0) { - byte[] buf = new byte[availableBytes]; - int bytesRead = input.read(buf); - while (bytesRead != -1) { - output.write(buf, 0, bytesRead); - bytesRead = input.read(buf); - log.trace("Bytes read: {}", bytesRead); - } - output.flush(); - } else { - log.debug("Nothing to available to copy"); - } - } - - /** - * Copies information between specified streams and then closes both of the streams. - * - * @param output output - * @param input input - * @throws java.io.IOException on error - */ - public static void copyThenClose(InputStream input, OutputStream output) throws IOException { - copy(input, output); - input.close(); - output.close(); - } - - /** - * @param input input stream - * @return a byte[] containing the information contained in the specified InputStream. - * @throws java.io.IOException on error - */ - public static byte[] getBytes(InputStream input) throws IOException { - ByteArrayOutputStream result = new ByteArrayOutputStream(); - copy(input, result); - result.close(); - return result.toByteArray(); - } - - /** - * Return all remote addresses that were involved in the passed request. - * - * @param request request - * @return remote addresses - */ - public static List getRemoteAddresses(HttpServletRequest request) { - List addresses = new ArrayList(); - addresses.add(request.getRemoteHost()); - if (!request.getRemoteAddr().equals(request.getRemoteHost())) { - // Store both remote host and remote address - addresses.add(request.getRemoteAddr()); - } - final String forwardedFor = request.getHeader("X-Forwarded-For"); - if (forwardedFor != null) { - // Also store address this request was forwarded for. - final String[] parts = forwardedFor.split(","); - for (String part : parts) { - addresses.add(part); - } - } - final String httpVia = request.getHeader("Via"); - if (httpVia != null) { - // Also store address this request was forwarded for. - final String[] parts = httpVia.split(","); - for (String part : parts) { - addresses.add(part); - } - } - return Collections.unmodifiableList(addresses); - } - -} diff --git a/src/main/java/org/red5/server/persistence/RamPersistence.java b/src/main/java/org/red5/server/persistence/RamPersistence.java deleted file mode 100644 index 4a0cf1c21..000000000 --- a/src/main/java/org/red5/server/persistence/RamPersistence.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.persistence; - -import java.util.Collection; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.red5.server.api.persistence.IPersistable; -import org.red5.server.api.persistence.IPersistenceStore; -import org.red5.server.api.scope.IScope; -import org.red5.server.util.ScopeUtils; -import org.springframework.core.io.support.ResourcePatternResolver; - -/** - * Persistence implementation that stores the objects in memory. - * This serves as default persistence if nothing has been configured. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * @author Michael Klishin (michael@novemberain.com) - */ -public class RamPersistence implements IPersistenceStore { - - /** This is used in the id for objects that have a name of null **/ - protected static final String PERSISTENCE_NO_NAME = "__null__"; - - /** - * Map for persistable objects - */ - protected ConcurrentMap objects = new ConcurrentHashMap(); - - /** - * Resource pattern resolver. Resolves resources from patterns, loads resources. - */ - protected ResourcePatternResolver resources; - - /** - * Creates RAM persistence object from resource pattern resolvers - * @param resources Resource pattern resolver and loader - */ - public RamPersistence(ResourcePatternResolver resources) { - this.resources = resources; - } - - /** - * Creates RAM persistence object from scope - * @param scope Scope - */ - public RamPersistence(IScope scope) { - this((ResourcePatternResolver) ScopeUtils.findApplication(scope)); - } - - /** - * Get resource name from path - * @param id Object ID. The format of the object id is //. - * @return Resource name - */ - protected String getObjectName(String id) { - // The format of the object id is // - String result = id.substring(id.lastIndexOf('/') + 1); - if (result.equals(PERSISTENCE_NO_NAME)) { - result = null; - } - return result; - } - - /** - * Get object path for given id and name - * @param id Object ID. The format of the object id is // - * @param name Object name - * @return Resource path - */ - protected String getObjectPath(String id, String name) { - // The format of the object id is // - id = id.substring(id.indexOf('/') + 1); - if (id.charAt(0) == '/') { - id = id.substring(1); - } - if (id.lastIndexOf(name) <= 0) { - return id; - } - return id.substring(0, id.lastIndexOf(name) - 1); - } - - /** - * Get object id - * @param object Persistable object whose id is asked for - * @return Given persistable object id - */ - protected String getObjectId(IPersistable object) { - // The format of the object id is // - String result = object.getType(); - if (object.getPath().charAt(0) != '/') { - result += '/'; - } - result += object.getPath(); - if (!result.endsWith("/")) { - result += '/'; - } - String name = object.getName(); - if (name == null) { - name = PERSISTENCE_NO_NAME; - } - if (name.charAt(0) == '/') { - // "result" already ends with a slash - name = name.substring(1); - } - return result + name; - } - - /** {@inheritDoc} */ - public boolean save(IPersistable object) { - final String key = getObjectId(object); - objects.put(key, object); - return true; - } - - /** {@inheritDoc} */ - public IPersistable load(String name) { - return objects.get(name); - } - - /** {@inheritDoc} */ - public boolean load(IPersistable obj) { - return obj.isPersistent(); - } - - /** {@inheritDoc} */ - public boolean remove(IPersistable object) { - return remove(getObjectId(object)); - } - - /** {@inheritDoc} */ - public boolean remove(String name) { - if (!objects.containsKey(name)) { - return false; - } - objects.remove(name); - return true; - } - - /** {@inheritDoc} */ - public Set getObjectNames() { - return objects.keySet(); - } - - /** {@inheritDoc} */ - public Collection getObjects() { - return objects.values(); - } - - /** {@inheritDoc} */ - public void notifyClose() { - objects.clear(); - } -} diff --git a/src/main/java/org/red5/server/plugin/PluginDescriptor.java b/src/main/java/org/red5/server/plugin/PluginDescriptor.java deleted file mode 100644 index 4a892cf59..000000000 --- a/src/main/java/org/red5/server/plugin/PluginDescriptor.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.plugin; - -import java.util.Map; - -/** - * Simple descriptor for plug-ins. - * - * @author Paul Gregoire - */ -public final class PluginDescriptor { - - private String pluginName; - - private String pluginType; - - private String method; - - private String methodReturnType; - - private Map properties; - - public String getPluginName() { - return pluginName; - } - - public void setPluginName(String pluginName) { - this.pluginName = pluginName; - } - - public String getPluginType() { - return pluginType; - } - - public void setPluginType(String pluginType) { - this.pluginType = pluginType; - } - - public String getMethod() { - return method; - } - - public void setMethod(String method) { - this.method = method; - } - - public String getMethodReturnType() { - return methodReturnType; - } - - public void setMethodReturnType(String methodReturnType) { - this.methodReturnType = methodReturnType; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - -} diff --git a/src/main/java/org/red5/server/scheduling/QuartzSchedulingService.java b/src/main/java/org/red5/server/scheduling/QuartzSchedulingService.java deleted file mode 100644 index 69a3e4308..000000000 --- a/src/main/java/org/red5/server/scheduling/QuartzSchedulingService.java +++ /dev/null @@ -1,407 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.scheduling; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicLong; - -import org.quartz.DateBuilder; -import org.quartz.DateBuilder.IntervalUnit; -import org.quartz.JobBuilder; -import org.quartz.JobDataMap; -import org.quartz.JobDetail; -import org.quartz.JobKey; -import org.quartz.Scheduler; -import org.quartz.SchedulerException; -import org.quartz.SchedulerFactory; -import org.quartz.SimpleScheduleBuilder; -import org.quartz.Trigger; -import org.quartz.TriggerBuilder; -import org.quartz.TriggerKey; -import org.quartz.impl.StdSchedulerFactory; -import org.red5.logging.Red5LoggerFactory; -import org.red5.server.api.scheduling.IScheduledJob; -import org.red5.server.api.scheduling.ISchedulingService; -import org.red5.server.jmx.mxbeans.QuartzSchedulingServiceMXBean; -import org.slf4j.Logger; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.scheduling.quartz.JobDetailFactoryBean; -import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; - -/** - * Scheduling service that uses Quartz as backend. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * @author Paul Gregoire (mondain@gmail.com) - */ -@ManagedResource(objectName = "org.red5.server:name=schedulingService,type=QuartzSchedulingService") -public class QuartzSchedulingService implements ISchedulingService, QuartzSchedulingServiceMXBean, InitializingBean, DisposableBean { - - private static Logger log = Red5LoggerFactory.getLogger(QuartzSchedulingService.class); - - /** - * Quartz configuration properties file - */ - protected String configFile; - - /** - * Number of job details - */ - protected AtomicLong jobDetailCounter = new AtomicLong(0); - - /** - * Creates schedulers. - */ - protected SchedulerFactory factory; - - /** - * Creates job detail. - */ - protected JobDetailFactoryBean jobDetailfactory; - - /** - * Creates triggers. - */ - protected SimpleTriggerFactoryBean triggerfactory; - - /** - * Service scheduler - */ - protected Scheduler scheduler; - - /** - * Instance id - */ - protected String instanceId; - - /** - * Default thread count - */ - protected String threadCount = "10"; - - /** - * Storage for job and trigger keys - */ - protected ConcurrentMap keyMap = new ConcurrentHashMap(); - - /** Constructs a new QuartzSchedulingService. */ - public void afterPropertiesSet() throws Exception { - log.debug("Initializing..."); - try { - //create the standard factory if we dont have one - if (factory == null) { - //set properties - if (configFile != null) { - factory = new StdSchedulerFactory(configFile); - } else { - Properties props = new Properties(); - props.put("org.quartz.scheduler.instanceName", "Red5_Scheduler"); - props.put("org.quartz.scheduler.instanceId", "AUTO"); - props.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); - props.put("org.quartz.threadPool.threadCount", threadCount); - props.put("org.quartz.threadPool.threadPriority", "5"); - props.put("org.quartz.jobStore.misfireThreshold", "60000"); - props.put("org.quartz.jobStore.class", "org.quartz.simpl.RAMJobStore"); - factory = new StdSchedulerFactory(props); - } - } - if (instanceId == null) { - scheduler = factory.getScheduler(); - } else { - scheduler = factory.getScheduler(instanceId); - } - //start the scheduler - if (scheduler != null) { - scheduler.start(); - } else { - log.error("Scheduler was not started"); - } - } catch (SchedulerException ex) { - throw new RuntimeException(ex); - } - } - - public void setFactory(SchedulerFactory factory) { - this.factory = factory; - } - - public void setInstanceId(String instanceId) { - this.instanceId = instanceId; - } - - public String getConfigFile() { - return configFile; - } - - public void setConfigFile(String configFile) { - this.configFile = configFile; - } - -// protected void registerJMX() { -// //register with jmx server -// MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); -// try { -// ObjectName oName = null; -// if (instanceId == null) { -// oName = new ObjectName("org.red5.server:name=" + this.getClass().getName()); -// } else { -// oName = new ObjectName("org.red5.server:name=" + this.getClass().getName() + ",instanceId=" + instanceId); -// } -// mbeanServer.registerMBean(this, oName); -// } catch (Exception e) { -// log.warn("Error on jmx registration", e); -// } -// } - - /** - * @return the threadCount - */ - public String getThreadCount() { - return threadCount; - } - - /** - * @param threadCount the threadCount to set - */ - public void setThreadCount(String threadCount) { - this.threadCount = threadCount; - } - - /** {@inheritDoc} */ - public String addScheduledJob(int interval, IScheduledJob job) { - String name = getJobName(); - // Store reference to applications job and service - JobDataMap jobData = new JobDataMap(); - jobData.put(QuartzSchedulingServiceJob.SCHEDULING_SERVICE, this); - jobData.put(QuartzSchedulingServiceJob.SCHEDULED_JOB, job); - // detail - JobDetail jobDetail = JobBuilder.newJob(QuartzSchedulingServiceJob.class) - .withIdentity(name) - .usingJobData(jobData) - .build(); - // create trigger that fires indefinitely every milliseconds - Trigger trigger = TriggerBuilder.newTrigger() - .withIdentity(String.format("Trigger_%s", name)) - .startAt(DateBuilder.futureDate(1, IntervalUnit.MILLISECOND)) - .forJob(jobDetail) - .withSchedule(SimpleScheduleBuilder.simpleSchedule() - .withIntervalInMilliseconds(interval) - .repeatForever()) - .build(); - // store keys by name - TriggerKey tKey = trigger.getKey(); - JobKey jKey = trigger.getJobKey(); - log.debug("Job key: {} Trigger key: {}", jKey, tKey); - ScheduledJobKey key = new ScheduledJobKey(tKey, jKey); - keyMap.put(name, key); - // schedule - scheduleJob(trigger, jobDetail); - return name; - } - - /** {@inheritDoc} */ - public String addScheduledOnceJob(Date date, IScheduledJob job) { - String name = getJobName(); - // Store reference to applications job and service - JobDataMap jobData = new JobDataMap(); - jobData.put(QuartzSchedulingServiceJob.SCHEDULING_SERVICE, this); - jobData.put(QuartzSchedulingServiceJob.SCHEDULED_JOB, job); - // detail - JobDetail jobDetail = JobBuilder.newJob(QuartzSchedulingServiceJob.class) - .withIdentity(name) - .usingJobData(jobData) - .build(); - // create trigger that fires once - Trigger trigger = TriggerBuilder.newTrigger() - .withIdentity(String.format("Trigger_%s", name)) - .startAt(date) - .forJob(jobDetail) - .build(); - log.debug("Job key: {} Trigger key: {}", trigger.getJobKey(), trigger.getKey()); - // store keys by name - TriggerKey tKey = trigger.getKey(); - JobKey jKey = trigger.getJobKey(); - log.debug("Job key: {} Trigger key: {}", jKey, tKey); - ScheduledJobKey key = new ScheduledJobKey(tKey, jKey); - keyMap.put(name, key); - // schedule - scheduleJob(trigger, jobDetail); - return name; - } - - /** {@inheritDoc} */ - public String addScheduledOnceJob(long timeDelta, IScheduledJob job) { - // Create trigger that fires once in milliseconds - return addScheduledOnceJob(new Date(System.currentTimeMillis() + timeDelta), job); - } - - /** {@inheritDoc} */ - public String addScheduledJobAfterDelay(int interval, IScheduledJob job, int delay) { - String name = getJobName(); - // Store reference to applications job and service - JobDataMap jobData = new JobDataMap(); - jobData.put(QuartzSchedulingServiceJob.SCHEDULING_SERVICE, this); - jobData.put(QuartzSchedulingServiceJob.SCHEDULED_JOB, job); - // detail - JobDetail jobDetail = JobBuilder.newJob(QuartzSchedulingServiceJob.class) - .withIdentity(name, null) - .usingJobData(jobData) - .build(); - // Create trigger that fires indefinitely every milliseconds - Trigger trigger = TriggerBuilder.newTrigger() - .withIdentity(String.format("Trigger_%s", name)) - .startAt(DateBuilder.futureDate(delay, IntervalUnit.MILLISECOND)) - .forJob(jobDetail) - .withSchedule(SimpleScheduleBuilder.simpleSchedule() - .withIntervalInMilliseconds(interval) - .repeatForever()) - .build(); - // store keys by name - TriggerKey tKey = trigger.getKey(); - JobKey jKey = trigger.getJobKey(); - log.debug("Job key: {} Trigger key: {}", jKey, tKey); - ScheduledJobKey key = new ScheduledJobKey(tKey, jKey); - keyMap.put(name, key); - // schedule - scheduleJob(trigger, jobDetail); - return name; - } - - /** - * Getter for job name. - * - * @return Job name - */ - public String getJobName() { - return String.format("ScheduledJob_%d", jobDetailCounter.getAndIncrement()); - } - - /** {@inheritDoc} */ - public List getScheduledJobNames() { - List result = new ArrayList(); - if (scheduler != null) { - try { - for (JobKey jobKey : scheduler.getJobKeys(null)) { - result.add(jobKey.getName()); - } - } catch (SchedulerException ex) { - throw new RuntimeException(ex); - } - } else { - log.warn("No scheduler is available"); - } - return result; - } - - /** {@inheritDoc} */ - public void pauseScheduledJob(String name) { - try { - scheduler.pauseJob(keyMap.get(name).jKey); - } catch (SchedulerException ex) { - throw new RuntimeException(ex); - } - } - - /** {@inheritDoc} */ - public void resumeScheduledJob(String name) { - try { - scheduler.resumeJob(keyMap.get(name).jKey); - } catch (SchedulerException ex) { - throw new RuntimeException(ex); - } - } - - public void pauseScheduledTrigger(String name) { - try { - scheduler.pauseTrigger(keyMap.get(name).tKey); - } catch (SchedulerException ex) { - throw new RuntimeException(ex); - } - } - - public void resumeScheduledTrigger(String name) { - try { - scheduler.resumeTrigger(keyMap.get(name).tKey); - } catch (SchedulerException ex) { - throw new RuntimeException(ex); - } - } - - /** {@inheritDoc} */ - public void removeScheduledJob(String name) { - try { - ScheduledJobKey key = keyMap.remove(name); - if (key != null) { - scheduler.deleteJob(key.jKey); - } else { - log.debug("No key found for job: {}", name); - } - } catch (SchedulerException ex) { - throw new RuntimeException(ex); - } - } - - /** - * Schedules job - * - * @param trigger Job trigger - * @param jobDetail Job detail - */ - private void scheduleJob(Trigger trigger, JobDetail jobDetail) { - if (scheduler != null) { - try { - scheduler.scheduleJob(jobDetail, trigger); - } catch (SchedulerException ex) { - throw new RuntimeException(ex); - } - } else { - log.warn("No scheduler is available"); - } - } - - public void destroy() throws Exception { - if (scheduler != null) { - log.debug("Destroying..."); - scheduler.shutdown(false); - } - keyMap.clear(); - } - - protected final class ScheduledJobKey { - - TriggerKey tKey; - - JobKey jKey; - - public ScheduledJobKey(TriggerKey tKey, JobKey jKey) { - this.tKey = tKey; - this.jKey = jKey; - } - - } - -} diff --git a/src/main/java/org/red5/server/scheduling/QuartzSchedulingServiceJob.java b/src/main/java/org/red5/server/scheduling/QuartzSchedulingServiceJob.java deleted file mode 100644 index 93efdbc4e..000000000 --- a/src/main/java/org/red5/server/scheduling/QuartzSchedulingServiceJob.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.scheduling; - -import org.quartz.JobDataMap; -import org.quartz.JobDetail; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.red5.server.api.scheduling.IScheduledJob; -import org.red5.server.api.scheduling.ISchedulingService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.scheduling.quartz.QuartzJobBean; - -/** - * Scheduled job that is registered in the Quartz scheduler. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public class QuartzSchedulingServiceJob extends QuartzJobBean { - - private Logger log = LoggerFactory.getLogger(QuartzSchedulingServiceJob.class); - - /** - * Scheduling service constant - */ - protected static final String SCHEDULING_SERVICE = "scheduling_service"; - - /** - * Scheduled job constant - */ - protected static final String SCHEDULED_JOB = "scheduled_job"; - - /** - * Job data map - */ - private JobDataMap jobDataMap; - - public void setJobDataMap(JobDataMap jobDataMap) { - log.debug("Set job data map: {}", jobDataMap); - this.jobDataMap = jobDataMap; - } - - public void execute() { - log.debug("execute"); - ISchedulingService service = null; - IScheduledJob job = null; - try { - service = (ISchedulingService) jobDataMap.get(SCHEDULING_SERVICE); - job = (IScheduledJob) jobDataMap.get(SCHEDULED_JOB); - job.execute(service); - } catch (Throwable e) { - if (job == null) { - log.error("Job not found"); - } else { - log.error("Job {} execution failed", job.toString(), e); - } - } - } - - /** {@inheritDoc} */ - @Override - protected void executeInternal(JobExecutionContext executionContext) throws JobExecutionException { - log.debug("execute: {}", executionContext); - ISchedulingService service = null; - IScheduledJob job = null; - try { - JobDetail jobDetail = executionContext.getJobDetail(); - JobDataMap dataMap = jobDetail.getJobDataMap(); - service = (ISchedulingService) dataMap.get(SCHEDULING_SERVICE); - job = (IScheduledJob) dataMap.get(SCHEDULED_JOB); - job.execute(service); - } catch (Throwable e) { - if (job == null) { - log.error("Job not found"); - } else { - log.error("Job {} execution failed", job.toString(), e); - } - } - } - -} diff --git a/src/main/java/org/red5/server/scope/BasicScope.java b/src/main/java/org/red5/server/scope/BasicScope.java deleted file mode 100644 index 1194be086..000000000 --- a/src/main/java/org/red5/server/scope/BasicScope.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.scope; - -import java.beans.ConstructorProperties; -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - -import org.red5.server.api.event.IEvent; -import org.red5.server.api.event.IEventListener; -import org.red5.server.api.persistence.IPersistenceStore; -import org.red5.server.api.scheduling.IScheduledJob; -import org.red5.server.api.scheduling.ISchedulingService; -import org.red5.server.api.scope.IBasicScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.ScopeType; -import org.red5.server.util.ScopeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Generalizations of one of main Red5 object types, Scope. - * - * @see org.red5.server.api.scope.IScope - * @see org.red5.server.scope.Scope - */ -public abstract class BasicScope implements IBasicScope, Comparable { - - protected static Logger log = LoggerFactory.getLogger(BasicScope.class); - - /** - * Parent scope. Scopes can be nested. - * - * @see org.red5.server.api.scope.IScope - */ - protected IScope parent; - - /** - * Scope type. - * - * @see org.red5.server.api.scope.ScopeType - */ - protected ScopeType type = ScopeType.UNDEFINED; - - /** - * String identifier for this scope - */ - protected String name; - - /** - * Creation timestamp - */ - protected long creation; - - /** - * Whether or not to persist attributes - */ - protected boolean persistent; - - /** - * Storage for persistable attributes - */ - protected IPersistenceStore store; - - /** - * Scope persistence storage type - */ - protected String persistenceClass; - - /** - * Set to true to prevent the scope from being freed upon disconnect. - */ - protected boolean keepOnDisconnect; - - /** - * Set to amount of time (in seconds) the scope will be kept before being freed, after the last disconnect. - */ - protected int keepDelay = 0; - - /** - * List of event listeners - */ - protected CopyOnWriteArraySet listeners; - - /** - * Scheduled job name for keep alive check - */ - private String keepAliveJobName; - - /** - * Creates unnamed scope - */ - @ConstructorProperties(value = { "" }) - public BasicScope() { - this.creation = System.nanoTime(); - } - - /** - * Constructor for basic scope - * - * @param parent Parent scope - * @param type Scope type - * @param name Scope name. Used to identify scopes in application, must be unique among scopes of one level - * @param persistent Whether scope is persistent - */ - @ConstructorProperties({ "parent", "type", "name", "persistent" }) - public BasicScope(IScope parent, ScopeType type, String name, boolean persistent) { - this.parent = parent; - this.type = type; - this.name = name; - this.persistent = persistent; - this.listeners = new CopyOnWriteArraySet(); - this.creation = System.nanoTime(); - } - - /** - * {@inheritDoc} - */ - public boolean hasParent() { - return true; - } - - /** - *{@inheritDoc} - */ - public IScope getParent() { - return parent; - } - - /** - * @return the type - */ - public ScopeType getType() { - return type; - } - - /** - * @return the name - */ - public String getName() { - return name; - } - - /** - * @return the store - */ - public IPersistenceStore getStore() { - return store; - } - - /** - *{@inheritDoc} - */ - public int getDepth() { - return parent.getDepth() + 1; - } - - /** - *{@inheritDoc} - */ - @Override - public String getPath() { - return parent.getPath() + '/' + parent.getName(); - } - - /** - * Sets the amount of time to keep the scope available after the - * last disconnect. - * - * @param keepDelay delay - */ - public void setKeepDelay(int keepDelay) { - this.keepDelay = keepDelay; - } - - /** - * Validates a scope based on its name and type - * - * @return true if both name and type are valid, false otherwise - */ - public boolean isValid() { - // to be valid a scope must have a type set other than undefined and its name will be set - return (type != null && !type.equals(ScopeType.UNDEFINED) && (name != null && !("").equals(name))); - } - - /** - * Add event listener to list of notified objects - * @param listener Listening object - * @return true if listener is added and false otherwise - */ - public boolean addEventListener(IEventListener listener) { - log.debug("addEventListener - scope: {} {}", getName(), listener); - return listeners.add(listener); - } - - /** - * Remove event listener from list of listeners - * @param listener Listener to remove - * @return true if listener is removed and false otherwise - */ - public boolean removeEventListener(IEventListener listener) { - log.debug("removeEventListener - scope: {} {}", getName(), listener); - if (log.isTraceEnabled()) { - log.trace("Listeners - check #1: {}", listeners); - } - boolean removed = listeners.remove(listener); - if (!keepOnDisconnect) { - if (removed && keepAliveJobName == null) { - if (ScopeUtils.isRoom(this) && listeners.isEmpty()) { - // create job to kill the scope off if no listeners join within the delay - ISchedulingService schedulingService = (ISchedulingService) parent.getContext().getBean(ISchedulingService.BEAN_NAME); - // by default keep a scope around for a fraction of a second - keepAliveJobName = schedulingService.addScheduledOnceJob((keepDelay > 0 ? keepDelay * 1000 : 100), new KeepAliveJob(this)); - } - } - } else { - log.trace("Scope: {} is exempt from removal when empty", getName()); - } - if (log.isTraceEnabled()) { - log.trace("Listeners - check #2: {}", listeners); - } - return removed; - } - - /** - * Return listeners list iterator - * - * @return Listeners list iterator - */ - public Set getEventListeners() { - return Collections.unmodifiableSet(listeners); - } - - /** - * Returns true if there are event listeners attached to - * this scope. - * - * @return true if it has listeners; else false. - */ - public boolean hasEventListeners() { - return !listeners.isEmpty(); - } - - /** - * Handles event. To be implemented in subclass realization - * - * @param event Event context - * @return Event handling result - */ - public boolean handleEvent(IEvent event) { - return false; - } - - /** - * Notifies listeners on event. Current implementation is empty. To be implemented in subclass realization - * @param event Event to broadcast - */ - public void notifyEvent(IEvent event) { - - } - - /** - * Dispatches event (notifies all listeners) - * - * @param event Event to dispatch - */ - public void dispatchEvent(IEvent event) { - for (IEventListener listener : listeners) { - if (event.getSource() == null || event.getSource() != listener) { - listener.notifyEvent(event); - } - } - } - - /** - * Hash code is based on the scope's name and type - * - * @return hash code - */ - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - - /** - * Equality is based on the scope's name and type - * - * @param obj - */ - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - BasicScope other = (BasicScope) obj; - if (hashCode() != other.hashCode()) { - return false; - } - return true; - } - - public int compareTo(BasicScope that) { - if (this.equals(that)) { - return 0; - } - return name.compareTo(that.getName()); - } - - /** - * Keeps the scope alive for a set number of seconds. - */ - private class KeepAliveJob implements IScheduledJob { - - private IBasicScope scope = null; - - KeepAliveJob(IBasicScope scope) { - this.scope = scope; - } - - public void execute(ISchedulingService service) { - if (listeners.isEmpty()) { - // delete empty rooms - log.trace("Removing {} from {}", scope.getName(), parent.getName()); - parent.removeChildScope(scope); - } - keepAliveJobName = null; - } - - } - -} diff --git a/src/main/java/org/red5/server/scope/Scope.java b/src/main/java/org/red5/server/scope/Scope.java deleted file mode 100644 index dd7b75776..000000000 --- a/src/main/java/org/red5/server/scope/Scope.java +++ /dev/null @@ -1,1464 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.scope; - -import java.beans.ConstructorProperties; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArraySet; - -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.management.StandardMBean; -import javax.management.openmbean.CompositeData; - -import org.apache.commons.lang3.StringUtils; -import org.red5.server.AttributeStore; -import org.red5.server.Server; -import org.red5.server.api.IClient; -import org.red5.server.api.IConnection; -import org.red5.server.api.IContext; -import org.red5.server.api.IServer; -import org.red5.server.api.event.IEvent; -import org.red5.server.api.persistence.PersistenceUtils; -import org.red5.server.api.scope.IBasicScope; -import org.red5.server.api.scope.IBroadcastScope; -import org.red5.server.api.scope.IGlobalScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeAware; -import org.red5.server.api.scope.IScopeHandler; -import org.red5.server.api.scope.ScopeType; -import org.red5.server.api.statistics.IScopeStatistics; -import org.red5.server.api.statistics.support.StatisticsCounter; -import org.red5.server.exception.ScopeException; -import org.red5.server.jmx.mxbeans.ScopeMXBean; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; -import org.springframework.jmx.export.annotation.ManagedResource; - -/** - * The scope object. - *

- * A stateful object shared between a group of clients connected to the same context path. Scopes are arranged in a hierarchical way, - * so a scope always has a parent unless its a "global" scope. If a client is connected to a scope then they are also connected to its - * parent scope. The scope object is used to access resources, shared object, streams, etc.

- *

- * Scope layout: - *

- *  /Global scope - Contains application scopes
- *      /Application scope - Contains room, shared object, and stream scopes
- *          /Room scope - Contains other room, shared object, and / or stream scopes
- *              /Shared object scope - Contains shared object
- *              /Broadcast stream scope - Contains a broadcast stream
- * 
- *

- * @author The Red5 Project - * @author Paul Gregoire (mondain@gmail.com) - * @author Nathan Smith (nathgs@gmail.com) - */ -@ManagedResource(objectName = "org.red5.server:type=Scope", description = "Scope") -public class Scope extends BasicScope implements IScope, IScopeStatistics, ScopeMXBean { - - protected static Logger log = LoggerFactory.getLogger(Scope.class); - - /** - * Unset flag constant - */ - private static final int UNSET = -1; - - /** - * Auto-start flag - */ - private boolean autoStart = true; - - /** - * Child scopes - */ - private final ConcurrentScopeSet children; - - /** - * Connected clients map - */ - private final CopyOnWriteArraySet clients; - - /** - * Storage for scope attributes - */ - protected final AttributeStore attributes = new AttributeStore(); - - /** - * Statistics about connections to the scope. - */ - protected final StatisticsCounter connectionStats = new StatisticsCounter(); - - /** - * Statistics about sub-scopes. - */ - protected final StatisticsCounter subscopeStats = new StatisticsCounter(); - - /** - * Scope context - */ - private IContext context; - - /** - * Timestamp the scope was created. - */ - private long creationTime; - - /** - * Scope nesting depth, unset by default - */ - private int depth = UNSET; - - /** - * Whether scope is enabled - */ - private boolean enabled = true; - - /** - * Scope handler - */ - private IScopeHandler handler; - - /** - * Whether scope is running - */ - private boolean running; - - /** - * Registered service handlers for this scope. The map is created on-demand - * only if it's accessed for writing. - */ - private volatile ConcurrentMap serviceHandlers; - - /** - * Mbean object name. - */ - protected ObjectName oName; - - { - creationTime = System.currentTimeMillis(); - } - - /** - * Creates a scope - */ - @ConstructorProperties(value = { "" }) - public Scope() { - super(null, ScopeType.UNDEFINED, null, false); - children = new ConcurrentScopeSet(); - clients = new CopyOnWriteArraySet(); - } - - /** - * Creates scope using a Builder - * - * @param builder - */ - @ConstructorProperties({ "builder" }) - public Scope(Builder builder) { - super(builder.parent, builder.type, builder.name, builder.persistent); - children = new ConcurrentScopeSet(); - clients = new CopyOnWriteArraySet(); - } - - /** - * Add child scope to this scope - * - * @param scope Child scope - * @return true on success (if scope has handler and it - * accepts child scope addition), false otherwise - */ - public boolean addChildScope(IBasicScope scope) { - log.debug("Add child: {}", scope); - boolean added = false; - if (scope.isValid()) { - try { - if (!children.containsKey(scope)) { - log.debug("Adding child scope: {} to {}", scope, this); - added = children.add(scope); - } else { - log.warn("Child scope already exists"); - } - } catch (Exception e) { - log.warn("Exception on add subscope", e); - } - } else { - log.warn("Invalid scope rejected: {}", scope); - } - if (added && scope.getStore() == null) { - // if child scope has no persistence store, use same class as parent - try { - if (scope instanceof Scope) { - ((Scope) scope).setPersistenceClass(persistenceClass); - } - } catch (Exception error) { - log.error("Could not set persistence class", error); - } - } - return added; - } - - /** - * Connect to scope - * - * @param conn Connection object - * @return true on success, false otherwise - */ - public boolean connect(IConnection conn) { - return connect(conn, null); - } - - /** - * Connect to scope with parameters. To successfully connect to scope it - * must have handler that will accept this connection with given set of - * parameters. Client associated with connection is added to scope clients set, - * connection is registered as scope event listener. - * - * @param conn Connection object - * @param params Parameters passed with connection - * @return true on success, false otherwise - */ - public boolean connect(IConnection conn, Object[] params) { - log.debug("Connect - scope: {} connection: {}", this, conn); - if (enabled) { - if (hasParent() && !parent.connect(conn, params)) { - log.debug("Connection to parent failed"); - return false; - } - if (hasHandler() && !getHandler().connect(conn, this, params)) { - log.debug("Connection to handler failed"); - return false; - } - if (!conn.isConnected()) { - log.debug("Connection is not connected"); - // timeout while connecting client - return false; - } - final IClient client = conn.getClient(); - // we would not get this far if there is no handler - if (hasHandler() && !getHandler().join(client, this)) { - return false; - } - // checking the connection again? why? - if (!conn.isConnected()) { - // timeout while connecting client - return false; - } - // add the client and event listener - if (clients.add(client) && addEventListener(conn)) { - log.debug("Added client"); - // increment conn stats - connectionStats.increment(); - // get connected scope - IScope connScope = conn.getScope(); - log.trace("Connection scope: {}", connScope); - if (this.equals(connScope)) { - final IServer server = getServer(); - if (server instanceof Server) { - ((Server) server).notifyConnected(conn); - } - } - return true; - } - } else { - log.debug("Connection failed, scope is disabled"); - } - return false; - } - - /** - * Create child scope of room type, with the given name. - * - * @param name child scope name - * @return true on success, false otherwise - */ - public boolean createChildScope(String name) { - // quick lookup by name - log.debug("createChildScope: {}", name); - if (children.hasName(name)) { - log.debug("Scope: {} already exists, children: {}", name, children.getNames()); - } else { - return addChildScope(new Builder(this, ScopeType.ROOM, name, false).build()); - } - return false; - } - - /** - * Destroys scope - * - * @throws Exception - */ - public void destroy() throws Exception { - log.debug("Destroy scope"); - if (hasParent()) { - parent.removeChildScope(this); - } - if (hasHandler()) { - // Because handler can be null when there is a parent handler - getHandler().stop(this); - } - // kill all child scopes - for (IBasicScope child : children.keySet()) { - removeChildScope(child); - if (child instanceof Scope) { - ((Scope) child).uninit(); - } - } - } - - /** - * Disconnect connection from scope - * - * @param conn Connection object - */ - public void disconnect(IConnection conn) { - log.debug("Disconnect: {}", conn); - // call disconnect handlers in reverse order of connection. ie. roomDisconnect is called before appDisconnect. - final IClient client = conn.getClient(); - if (client == null) { - // early bail out - removeEventListener(conn); - connectionStats.decrement(); - if (hasParent()) { - parent.disconnect(conn); - } - return; - } - // remove it if it exists - if (clients.remove(client)) { - IScopeHandler handler = getHandler(); - if (handler != null) { - try { - handler.disconnect(conn, this); - } catch (Exception e) { - log.error("Error while executing \"disconnect\" for connection {} on handler {}. {}", new Object[] { conn, handler, e }); - } - try { - // there may be a timeout here ? - handler.leave(client, this); - } catch (Exception e) { - log.error("Error while executing \"leave\" for client {} on handler {}. {}", new Object[] { conn, handler, e }); - } - } - // remove listener - removeEventListener(conn); - // decrement if there was a set of connections - connectionStats.decrement(); - if (this.equals(conn.getScope())) { - final IServer server = getServer(); - if (server instanceof Server) { - ((Server) server).notifyDisconnected(conn); - } - } - } - if (hasParent()) { - parent.disconnect(conn); - } - } - - /** {@inheritDoc} */ - @Override - public void dispatchEvent(IEvent event) { - Set conns = getClientConnections(); - for (IConnection conn : conns) { - try { - conn.dispatchEvent(event); - } catch (RuntimeException e) { - log.error("Exception during dispatching event: {}", event, e); - } - } - } - - /** {@inheritDoc} */ - public Object getAttribute(String name) { - return attributes.getAttribute(name); - } - - /** {@inheritDoc} */ - public boolean setAttribute(String name, Object value) { - return attributes.setAttribute(name, value); - } - - /** {@inheritDoc} */ - public boolean hasAttribute(String name) { - return attributes.hasAttribute(name); - } - - /** {@inheritDoc} */ - public boolean removeAttribute(String name) { - return attributes.removeAttribute(name); - } - - /** {@inheritDoc} */ - public Set getAttributeNames() { - return attributes.getAttributeNames(); - } - - /** {@inheritDoc} */ - public Map getAttributes() { - return attributes.getAttributes(); - } - - /** {@inheritDoc} */ - public int getActiveClients() { - return clients.size(); - } - - /** {@inheritDoc} */ - public int getActiveConnections() { - return connectionStats.getCurrent(); - } - - /** {@inheritDoc} */ - public int getActiveSubscopes() { - return subscopeStats.getCurrent(); - } - - /** - * Return the broadcast scope for a given name - * - * @param name - * @return broadcast scope or null if not found - */ - public IBroadcastScope getBroadcastScope(String name) { - return (IBroadcastScope) children.getBasicScope(ScopeType.BROADCAST, name); - } - - /** - * Return base scope of given type with given name - * - * @param type Scope type - * @param name Scope name - * @return Basic scope object - */ - public IBasicScope getBasicScope(ScopeType type, String name) { - return children.getBasicScope(type, name); - } - - /** - * Return basic scope names matching given type - * - * @param type Scope type - * @return set of scope names - */ - public Set getBasicScopeNames(ScopeType type) { - if (type != null) { - Set names = new HashSet(); - for (IBasicScope child : children.keySet()) { - if (child.getType().equals(type)) { - names.add(child.getName()); - } - } - return names; - } - return getScopeNames(); - } - - /** - * Return current thread context classloader - * - * @return Current thread context classloader - */ - public ClassLoader getClassLoader() { - return getContext().getClassLoader(); - } - - /** - * Return set of clients - * - * @return Set of clients bound to scope - */ - public Set getClients() { - return clients; - } - - /** {@inheritDoc} */ - @Deprecated - public Collection> getConnections() { - Collection> result = new ArrayList>(3); - result.add(getClientConnections()); - return result; - } - - /** {@inheritDoc} */ - public Set getClientConnections() { - Set result = new HashSet(3); - log.debug("Client count: {}", clients.size()); - for (IClient cli : clients) { - Set set = cli.getConnections(); - log.debug("Client connection count: {}", set.size()); - if (set.size() > 1) { - log.warn("Client connections exceeded expected single count; size: {}", set.size()); - } - for (IConnection conn : set) { - result.add(conn); - } - } - return result; - } - - /** {@inheritDoc} */ - @Deprecated - public Set lookupConnections(IClient client) { - HashSet result = new HashSet(1); - if (clients.contains(client)) { - for (IClient cli : clients) { - if (cli.equals(client)) { - Set set = cli.getConnections(); - if (set.size() > 1) { - log.warn("Client connections exceeded expected single count; size: {}", set.size()); - } - result.add(set.iterator().next()); - break; - } - } - } - return result; - } - - /** {@inheritDoc} */ - public IConnection lookupConnection(IClient client) { - for (IClient cli : clients) { - if (cli.equals(client)) { - Set set = cli.getConnections(); - if (set.size() > 1) { - log.warn("Client connections exceeded expected single count; size: {}", set.size()); - } - return set.iterator().next(); - } - } - return null; - } - - /** - * Return scope context. If scope doesn't have context, parent's context is - * returns, and so forth. - * - * @return Scope context or parent context - */ - public IContext getContext() { - if (!hasContext() && hasParent()) { - //log.debug("returning parent context"); - return parent.getContext(); - } else { - //log.debug("returning context"); - return context; - } - } - - /** - * Return scope context path - * - * @return Scope context path - */ - public String getContextPath() { - if (hasContext()) { - return ""; - } else if (hasParent()) { - return parent.getContextPath() + '/' + name; - } else { - return null; - } - } - - /** {@inheritDoc} */ - public long getCreationTime() { - return creationTime; - } - - /** - * return scope depth - * - * @return Scope depth - */ - @Override - public int getDepth() { - if (depth == UNSET) { - if (hasParent()) { - depth = parent.getDepth() + 1; - } else { - depth = 0; - } - } - return depth; - } - - /** - * Return scope handler or parent's scope handler if this scope doesn't have one. - * - * @return Scope handler (or parent's one) - */ - public IScopeHandler getHandler() { - log.trace("getHandler from {}", name); - if (handler != null) { - return handler; - } else if (hasParent()) { - return getParent().getHandler(); - } else { - return null; - } - } - - /** {@inheritDoc} */ - @Deprecated - public int getMaxClients() { - return connectionStats.getMax(); - } - - /** {@inheritDoc} */ - public int getMaxConnections() { - return connectionStats.getMax(); - } - - /** {@inheritDoc} */ - public int getMaxSubscopes() { - return subscopeStats.getMax(); - } - - /** - * Return parent scope - * - * @return Parent scope - */ - @Override - public IScope getParent() { - return parent; - } - - /** - * Return scope path calculated from parent path and parent scope name - * - * @return Scope path - */ - @Override - public String getPath() { - if (hasParent()) { - return parent.getPath() + '/' + parent.getName(); - } else { - return ""; - } - } - - /** - * Return resource located at given path - * - * @param path Resource path - * @return Resource - */ - public Resource getResource(String path) { - if (hasContext()) { - return context.getResource(path); - } - return getContext().getResource(getContextPath() + '/' + path); - } - - /** - * Return array of resources from path string, usually used with pattern path - * - * @param path Resources path - * @return Resources - * @throws IOException I/O exception - */ - public Resource[] getResources(String path) throws IOException { - if (hasContext()) { - return context.getResources(path); - } - return getContext().getResources(getContextPath() + '/' + path); - } - - /** - * Return child scope by name - * - * @param name Scope name - * @return Child scope with given name - */ - public IScope getScope(String name) { - IBasicScope child = children.getBasicScope(ScopeType.UNDEFINED, name); - log.debug("Child of {}: {}", this.name, child); - if (child != null) { - if (child instanceof IScope) { - return (IScope) child; - } - log.warn("Requested scope: {} is not of IScope type: {}", name, child.getClass().getName()); - } - return null; - } - - /** - * Return child scope names iterator - * - * @return Child scope names iterator - */ - public Set getScopeNames() { - log.debug("Children: {}", children); - return children.getNames(); - } - - /** - * Return service handler by name - * - * @param name Handler name - * @return Service handler with given name - */ - public Object getServiceHandler(String name) { - Map serviceHandlers = getServiceHandlers(false); - if (serviceHandlers == null) { - return null; - } - return serviceHandlers.get(name); - } - - /** - * Return set of service handler names. Removing entries from the set - * unregisters the corresponding service handler. - * - * @return Set of service handler names - */ - @SuppressWarnings("unchecked") - public Set getServiceHandlerNames() { - Map serviceHandlers = getServiceHandlers(false); - if (serviceHandlers == null) { - return Collections.EMPTY_SET; - } - return serviceHandlers.keySet(); - } - - /** - * Return map of service handlers. The map is created if it doesn't exist yet. - * - * @return Map of service handlers - */ - protected Map getServiceHandlers() { - return getServiceHandlers(true); - } - - /** - * Return map of service handlers and optionally created it if it doesn't exist. - * - * @param allowCreate - * Should the map be created if it doesn't exist? - * @return Map of service handlers - */ - protected Map getServiceHandlers(boolean allowCreate) { - if (serviceHandlers == null) { - if (allowCreate) { - serviceHandlers = new ConcurrentHashMap(3, 0.9f, 1); - } - } - return serviceHandlers; - } - - /** {@inheritDoc} */ - public IScopeStatistics getStatistics() { - return this; - } - - /** {@inheritDoc} */ - @Deprecated - public int getTotalClients() { - return connectionStats.getTotal(); - } - - /** {@inheritDoc} */ - public int getTotalConnections() { - return connectionStats.getTotal(); - } - - /** {@inheritDoc} */ - public int getTotalSubscopes() { - return subscopeStats.getTotal(); - } - - /** - * Handles event. To be implemented in subclasses. - * - * @param event Event to handle - * @return true on success, false otherwise - */ - @Override - public boolean handleEvent(IEvent event) { - return false; - } - - /** - * Check whether scope has child scope with given name - * - * @param name Child scope name - * @return true if scope has child node with given name, - * false otherwise - */ - public boolean hasChildScope(String name) { - log.debug("Has child scope? {} in {}", name, this); - return children.hasName(name); - } - - /** - * Check whether scope has child scope with given name and type - * - * @param type Child scope type - * @param name Child scope name - * @return true if scope has child node with given name and - * type, false otherwise - */ - public boolean hasChildScope(ScopeType type, String name) { - log.debug("Has child scope? {} in {}", name, this); - return children.getBasicScope(type, name) != null; - } - - /** - * Check if scope has a context - * - * @return true if scope has context, false - * otherwise - */ - public boolean hasContext() { - return context != null; - } - - /** - * Check if scope or it's parent has handler - * - * @return true if scope or it's parent scope has a handler, - * false otherwise - */ - public boolean hasHandler() { - return (handler != null || (hasParent() && getParent().hasHandler())); - } - - /** - * Check if scope has parent scope - * - * @return true if scope has parent scope, false - * otherwise` - */ - @Override - public boolean hasParent() { - return (parent != null); - } - - /** - * Initialization actions, start if autostart is set to true - */ - public void init() { - log.debug("Init scope: {} parent: {}", name, parent); - if (hasParent()) { - if (!parent.hasChildScope(name)) { - if (parent.addChildScope(this)) { - log.debug("Scope added to parent"); - } else { - log.warn("Scope not added to parent"); - //throw new ScopeException("Scope not added to parent"); - return; - } - } else { - throw new ScopeException("Scope already exists in parent"); - } - } else { - log.debug("Scope has no parent"); - } - if (autoStart) { - start(); - } - } - - /** - * Uninitialize scope and unregister from parent. - */ - public void uninit() { - log.debug("Un-init scope"); - for (IBasicScope child : children.keySet()) { - if (child instanceof Scope) { - ((Scope) child).uninit(); - } - } - stop(); - setEnabled(false); - if (hasParent()) { - if (parent.hasChildScope(name)) { - parent.removeChildScope(this); - } - } - } - - /** - * Check if scope is enabled - * - * @return true if scope is enabled, false - * otherwise - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Here for JMX only, uses isEnabled() - */ - public boolean getEnabled() { - return isEnabled(); - } - - /** - * Check if scope is in running state - * - * @return true if scope is in running state, - * false otherwise - */ - public boolean isRunning() { - return running; - } - - /** - * Here for JMX only, uses isEnabled() - */ - public boolean getRunning() { - return isRunning(); - } - - /** - * Register service handler by name - * - * @param name Service handler name - * @param handler Service handler - */ - public void registerServiceHandler(String name, Object handler) { - Map serviceHandlers = getServiceHandlers(); - serviceHandlers.put(name, handler); - } - - /** - * Removes child scope - * - * @param scope Child scope to remove - */ - public void removeChildScope(IBasicScope scope) { - log.debug("removeChildScope: {}", scope); - if (children.containsKey(scope)) { - // remove from parent - children.remove(scope); - if (scope instanceof Scope) { - unregisterJMX(); - } - } - } - - /** - * Removes all the child scopes - */ - public void removeChildren() { - log.trace("removeChildren of {}", name); - for (IBasicScope child : children.keySet()) { - removeChildScope(child); - } - } - - /** - * Setter for autostart flag - * - * @param autoStart Autostart flag value - */ - public void setAutoStart(boolean autoStart) { - this.autoStart = autoStart; - } - - /** - * Setter for child load path. Should be implemented in subclasses? - * - * @param pattern Load path pattern - */ - public void setChildLoadPath(String pattern) { - - } - - /** - * Setter for context - * - * @param context Context object - */ - public void setContext(IContext context) { - log.debug("Set context: {}", context); - this.context = context; - } - - /** - * Set scope depth - * - * @param depth Scope depth - */ - public void setDepth(int depth) { - this.depth = depth; - } - - /** - * Enable or disable scope by setting enable flag - * - * @param enabled Enable flag value - */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - /** - * Setter for scope event handler - * - * @param handler Event handler - */ - public void setHandler(IScopeHandler handler) { - log.debug("setHandler: {} on {}", handler, name); - this.handler = handler; - if (handler instanceof IScopeAware) { - ((IScopeAware) handler).setScope(this); - } - } - - /** - * Setter for scope name - * - * @param name Scope name - */ - @Override - public void setName(String name) { - log.debug("Set name: {}", name); - if (oName != null) { - unregisterJMX(); - } - this.name = name; - if (StringUtils.isNotBlank(name)) { - registerJMX(); - } - } - - /** - * Setter for parent scope - * - * @param parent Parent scope - */ - public void setParent(IScope parent) { - log.debug("Set parent scope: {}", parent); - this.parent = parent; - } - - /** - * Set scope persistence class - * - * @param persistenceClass Scope's persistence class - * @throws Exception Exception - */ - public void setPersistenceClass(String persistenceClass) throws Exception { - this.persistenceClass = persistenceClass; - if (persistenceClass != null) { - store = PersistenceUtils.getPersistenceStore(this, persistenceClass); - } - } - - /** - * Starts scope - * - * @return true if scope has handler and it's start method - * returned true, false otherwise - */ - public boolean start() { - log.debug("Start scope"); - boolean result = false; - if (enabled && !running) { - // check for any handlers - if (handler != null) { - log.debug("Scope {} has a handler {}", this.getName(), handler); - } else { - log.debug("Scope {} has no handler", this); - handler = parent.getHandler(); - } - try { - // if we dont have a handler of our own dont try to start it - if (handler != null) { - result = handler.start(this); - } else { - // always start scopes without handlers - log.debug("Scope {} has no handler of its own, allowing start", this); - result = true; - } - } catch (Throwable e) { - log.error("Could not start scope {} {}", this, e); - } finally { - // post notification - ((Server) getServer()).notifyScopeCreated(this); - } - running = result; - } - return result; - } - - /** - * Stops scope - */ - public void stop() { - log.debug("stop: {}", name); - if (enabled && running && handler != null) { - try { - // if we dont have a handler of our own dont try to stop it - handler.stop(this); - } catch (Throwable e) { - log.error("Could not stop scope {}", this, e); - } finally { - // post notification - ((Server) getServer()).notifyScopeRemoved(this); - } - // remove all children - removeChildren(); - } - running = false; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "Scope [name=" + getName() + ", path=" + getPath() + ", type=" + type + ", autoStart=" + autoStart + ", creationTime=" + creationTime + ", depth=" + getDepth() - + ", enabled=" + enabled + ", running=" + running + "]"; - } - - /** - * Unregisters service handler by name - * - * @param name Service handler name - */ - public void unregisterServiceHandler(String name) { - Map serviceHandlers = getServiceHandlers(false); - if (serviceHandlers != null) { - serviceHandlers.remove(name); - } - } - - /** - * Return the server instance connected to this scope. - * - * @return the server instance - */ - public IServer getServer() { - if (hasParent()) { - final IScope parent = getParent(); - if (parent instanceof Scope) { - return ((Scope) parent).getServer(); - } else if (parent instanceof IGlobalScope) { - return ((IGlobalScope) parent).getServer(); - } - } - return null; - } - - //for debugging - public void dump() { - if (log.isTraceEnabled()) { - log.trace("Scope: {} {}", this.getClass().getName(), this); - log.trace("Running: {}", running); - if (hasParent()) { - log.trace("Parent: {}", parent); - Set names = parent.getBasicScopeNames(null); - log.trace("Sibling count: {}", names.size()); - for (String sib : names) { - log.trace("Siblings - {}", sib); - } - names = null; - } - log.trace("Handler: {}", handler); - log.trace("Child count: {}", children.size()); - for (IBasicScope child : children.keySet()) { - log.trace("Child: {}", child); - } - } - } - - protected void registerJMX() { - // register with jmx - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - try { - String cName = this.getClass().getName(); - if (cName.indexOf('.') != -1) { - cName = cName.substring(cName.lastIndexOf('.')).replaceFirst("[\\.]", ""); - } - oName = new ObjectName(String.format("org.red5.server:type=%s,name=%s", cName, name)); - // don't reregister - if (!mbs.isRegistered(oName)) { - mbs.registerMBean(new StandardMBean(this, ScopeMXBean.class, true), oName); - } - } catch (Exception e) { - log.warn("Error on jmx registration", e); - } - } - - protected void unregisterJMX() { - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - if (oName != null && mbs.isRegistered(oName)) { - try { - mbs.unregisterMBean(oName); - } catch (Exception e) { - log.warn("Exception unregistering: {}", oName, e); - } - oName = null; - } - } - - /** - * Allows for reconstruction via CompositeData. - * - * @param cd composite data - * @return Scope class instance - */ - public static Scope from(CompositeData cd) { - IScope parent = null; - ScopeType type = ScopeType.UNDEFINED; - String name = null; - boolean persistent = false; - if (cd.containsKey("parent")) { - parent = (IScope) cd.get("parent"); - } - if (cd.containsKey("type")) { - type = (ScopeType) cd.get("type"); - } - if (cd.containsKey("name")) { - name = (String) cd.get("name"); - } - if (cd.containsKey("persistent")) { - persistent = (Boolean) cd.get("persistent"); - } - return new Scope(new Builder(parent, type, name, persistent)); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((getPath() == null) ? 0 : getPath().hashCode()); - result = prime * result + getDepth(); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Scope other = (Scope) obj; - if (hashCode() != other.hashCode()) { - return false; - } - return true; - } - - private final class ConcurrentScopeSet extends ConcurrentHashMap { - - private static final long serialVersionUID = -6702012956307490749L; - - ConcurrentScopeSet() { - super(3, 0.9f); - } - - public boolean add(IBasicScope scope) { - //log.trace("add - Hold counts - read: {} write: {} queued: {}", internalLock.getReadHoldCount(), internalLock.getWriteHoldCount(), internalLock.hasQueuedThreads()); - boolean added = false; - // check #1 - if (!containsKey(scope)) { - log.debug("Adding child scope: {} to {}", (((IBasicScope) scope).getName()), this); - if (hasHandler()) { - // get the handler for the scope to which we are adding this new scope - IScopeHandler hdlr = getHandler(); - // add the scope to the handler - if (!hdlr.addChildScope(scope)) { - log.warn("Failed to add child scope: {} to {}", scope, this); - return false; - } - } else { - log.debug("No handler found for {}", this); - } - try { - // check #2 for entry - if (!containsKey(scope)) { - // add the entry - // expected return from put is null; indicating this scope didn't already exist - added = (super.put(scope, Boolean.TRUE) == null); - if (added) { - subscopeStats.increment(); - } else { - log.debug("Subscope was not added"); - } - } else { - log.debug("Subscope already exists"); - } - } catch (Exception e) { - log.warn("Exception on add", e); - } - if (added && scope instanceof Scope) { - // cast it - Scope scp = (Scope) scope; - // start the scope - if (scp.start()) { - log.debug("Child scope started"); - } else { - log.debug("Failed to start child scope: {} in {}", scope, this); - } - } - } - return added; - } - - @Override - public Boolean remove(Object scope) { - log.debug("Remove child scope: {}", scope); - //log.trace("remove - Hold counts - read: {} write: {} queued: {}", internalLock.getReadHoldCount(), internalLock.getWriteHoldCount(), internalLock.hasQueuedThreads()); - if (hasHandler()) { - IScopeHandler hdlr = getHandler(); - log.debug("Removing child scope: {}", (((IBasicScope) scope).getName())); - hdlr.removeChildScope((IBasicScope) scope); - if (scope instanceof Scope) { - // cast it - Scope scp = (Scope) scope; - // stop the scope - scp.stop(); - } - } else { - log.debug("No handler found for {}", this); - } - boolean removed = false; - try { - if (containsKey(scope)) { - // remove the entry, ensure removed value is equal to the given object - removed = super.remove(scope).equals(Boolean.TRUE); - if (removed) { - subscopeStats.decrement(); - } else { - log.debug("Subscope was not removed"); - } - } else { - log.debug("Subscope was not removed, it was not found"); - } - } catch (Exception e) { - log.warn("Exception on remove", e); - } - return removed; - } - - /** - * Returns whether or not a scope is in the collection; we're only concerned with keys. - * - * @param scope - * @return true if it exists and false otherwise - */ - public boolean contains(Object scope) { - return keySet().contains(scope); - } - - /** - * Returns the scope names. - * - * @return names - */ - public Set getNames() { - Set names = new HashSet(); - for (IBasicScope child : keySet()) { - names.add(child.getName()); - } - return names; - } - - /** - * Returns whether or not a named scope exists. - * - * @return true if a matching scope is found, false otherwise - */ - public boolean hasName(String name) { - for (IBasicScope child : keySet()) { - if (name.equals(child.getName())) { - return true; - } - } - return false; - } - - /** - * Returns a child scope for a given name and type. - * - * @param type Scope type - * @param name Scope name - * @return scope - */ - public IBasicScope getBasicScope(ScopeType type, String name) { - boolean skipTypeCheck = ScopeType.UNDEFINED.equals(type); - if (skipTypeCheck) { - for (IBasicScope child : keySet()) { - if (name.equals(child.getName())) { - log.debug("Returning basic scope: {}", child); - return child; - } - } - } else { - for (IBasicScope child : keySet()) { - if (child.getType().equals(type) && name.equals(child.getName())) { - log.debug("Returning basic scope: {}", child); - return child; - } - } - } - return null; - } - - } - - /** - * Builder pattern - */ - public final static class Builder { - private IScope parent; - - private ScopeType type; - - private String name; - - private boolean persistent; - - public Builder(IScope parent, ScopeType type, String name, boolean persistent) { - this.parent = parent; - this.type = type; - this.name = name; - this.persistent = persistent; - } - - public Scope build() { - return new Scope(this); - } - - } - -} diff --git a/src/main/java/org/red5/server/service/Call.java b/src/main/java/org/red5/server/service/Call.java deleted file mode 100644 index bb23fef35..000000000 --- a/src/main/java/org/red5/server/service/Call.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.service; - -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -import org.red5.server.api.service.IServiceCall; - -/** - * Basic service call (remote call) implementation - * - * @author The Red5 Project - * @author Luke Hubbard, Codegent Ltd (luke@codegent.com) - */ -public class Call implements IServiceCall, Externalizable { - - private static final long serialVersionUID = -3699712251588013875L; - - /** - * Pending status constant - */ - public static final byte STATUS_PENDING = 0x01; - - /** - * Success result constant - */ - public static final byte STATUS_SUCCESS_RESULT = 0x02; - - /** - * Returned value is null constant - */ - public static final byte STATUS_SUCCESS_NULL = 0x03; - - /** - * Service returns no value constant - */ - public static final byte STATUS_SUCCESS_VOID = 0x04; - - /** - * Service not found constant - */ - public static final byte STATUS_SERVICE_NOT_FOUND = 0x10; - - /** - * Service's method not found constant - */ - public static final byte STATUS_METHOD_NOT_FOUND = 0x11; - - /** - * Access denied constant - */ - public static final byte STATUS_ACCESS_DENIED = 0x12; - - /** - * Exception on invocation constant - */ - public static final byte STATUS_INVOCATION_EXCEPTION = 0x13; - - /** - * General exception constant - */ - public static final byte STATUS_GENERAL_EXCEPTION = 0x14; - - /** - * The application for this service is currently shutting down - */ - public static final byte STATUS_APP_SHUTTING_DOWN = 0x15; - - /** - * The remote method cannot be invoked because the client is not connected. NOTE that it is possible - * that this error is returned in the situation where the method has been invoked on the server the connection - * has failed before the result returned could be read. There is no way to establish whether this has happened. - */ - public static final byte STATUS_NOT_CONNECTED = 0x20; - - /** - * Service name - */ - protected String serviceName; - - /** - * Service method name - */ - protected String serviceMethodName; - - /** - * Call arguments - */ - protected Object[] arguments = null; - - /** - * Call status, initial one is pending - */ - protected byte status = STATUS_PENDING; - - /** - * Call exception if any, null by default - */ - protected Exception exception; - - /** - * Timestamp at which the call was deserialized. - */ - private long readTime; - - /** - * Timestamp at which the call was serialized. - */ - private long writeTime; - - public Call() { - } - - /** - * Creates call from method name - * @param method Method name - */ - public Call(String method) { - serviceMethodName = method; - } - - /** - * Creates call from method name and array of call parameters - * @param method Method name - * @param args Call parameters - */ - public Call(String method, Object[] args) { - serviceMethodName = method; - arguments = args; - } - - /** - * Creates call from given service name, method name and array of call parameters - * @param name Service name - * @param method Service method name - * @param args Call parameters - */ - public Call(String name, String method, Object[] args) { - serviceName = name; - serviceMethodName = method; - arguments = args; - } - - /** - * {@inheritDoc} - */ - public boolean isSuccess() { - return (status == STATUS_SUCCESS_RESULT) || (status == STATUS_SUCCESS_NULL) || (status == STATUS_SUCCESS_VOID); - } - - /** - * {@inheritDoc} - */ - public String getServiceMethodName() { - return serviceMethodName; - } - - /** - * Setter for service method name - * - * @param serviceMethodName New service method name value - */ - public void setServiceMethodName(String serviceMethodName) { - this.serviceMethodName = serviceMethodName; - } - - /** - * {@inheritDoc} - */ - public String getServiceName() { - return serviceName; - } - - /** - * Setter for service name - * - * @param serviceName New service name value - */ - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - /** - * {@inheritDoc} - */ - public Object[] getArguments() { - return arguments; - } - - /** - * Setter for arguments. - * - * @param args Arguments. - */ - public void setArguments(Object[] args) { - arguments = args; - } - - /** - * {@inheritDoc} - */ - public byte getStatus() { - return status; - } - - /** - * {@inheritDoc} - */ - public void setStatus(byte status) { - this.status = status; - } - - /** - * {@inheritDoc} - */ - public long getReadTime() { - return readTime; - } - - /** - * {@inheritDoc} - */ - public long getWriteTime() { - return writeTime; - } - - /** - * {@inheritDoc} - */ - public Exception getException() { - return exception; - } - - /** - * {@inheritDoc} - */ - public void setException(Exception exception) { - this.exception = exception; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuilder sb = new StringBuilder("Service: "); - sb.append(serviceName); - sb.append(" Method: "); - sb.append(serviceMethodName); - if (arguments != null) { - sb.append(" Num Params: "); - sb.append(arguments.length); - for (int i = 0; i < arguments.length; i++) { - sb.append(' '); - sb.append(i); - sb.append(": "); - sb.append(arguments[i]); - } - } else { - sb.append(" No params"); - } - return sb.toString(); - } - - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - // keep a time of receipt - readTime = System.currentTimeMillis(); - // read-in properties - serviceName = (String) in.readObject(); - serviceMethodName = (String) in.readObject(); - arguments = (Object[]) in.readObject(); - status = in.readByte(); - exception = (Exception) in.readObject(); - } - - public void writeExternal(ObjectOutput out) throws IOException { - // keep a time of receipt - writeTime = System.currentTimeMillis(); - // write-out properties - out.writeObject(serviceName); - out.writeObject(serviceMethodName); - out.writeObject(arguments); - out.writeByte(status); - out.writeObject(exception); - } -} diff --git a/src/main/java/org/red5/server/service/IServiceResolver.java b/src/main/java/org/red5/server/service/IServiceResolver.java deleted file mode 100644 index 1abf135c8..000000000 --- a/src/main/java/org/red5/server/service/IServiceResolver.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.service; - -import org.red5.server.api.scope.IScope; - -/** - * Interface for objects that resolve service names to services. - * - * This is used by the ServiceInvoker to lookup the service to invoke - * a method on. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IServiceResolver { - - /** - * Search for a service with the given name in the scope. - * - * @param scope the scope to search in - * @param serviceName the name of the service - * @return the object implementing the service or null if - * service doesn't exist - */ - public Object resolveService(IScope scope, String serviceName); - -} diff --git a/src/main/java/org/red5/server/service/MethodNotFoundException.java b/src/main/java/org/red5/server/service/MethodNotFoundException.java deleted file mode 100644 index cc6d0a2fb..000000000 --- a/src/main/java/org/red5/server/service/MethodNotFoundException.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.service; - -import java.util.Arrays; - -/** - * Thrown if service method is not found so call throws exception - * - * @author The Red5 Project - * @author Luke Hubbard, Codegent Ltd (luke@codegent.com) - */ -public class MethodNotFoundException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = 7559230924102506068L; - - /** - * Creates exception with given method name - * @param methodName Service method name that can't be found - */ - public MethodNotFoundException(String methodName) { - super("Method " + methodName + " without arguments not found"); - } - - /** - * Creates exception with given method name and arguments - * @param methodName Service method name that can't be found - * @param args Arguments given - */ - public MethodNotFoundException(String methodName, Object[] args) { - super("Method " + methodName + " with arguments " + Arrays.asList(args) + " not found"); - } - -} diff --git a/src/main/java/org/red5/server/service/NotAllowedException.java b/src/main/java/org/red5/server/service/NotAllowedException.java deleted file mode 100644 index b4a6451af..000000000 --- a/src/main/java/org/red5/server/service/NotAllowedException.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.service; - -/** - * Thrown when a client is not allowed to execute a method. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public class NotAllowedException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = -7552833324276839926L; - - public NotAllowedException() { - super(); - } - - public NotAllowedException(String message) { - super(message); - } - -} diff --git a/src/main/java/org/red5/server/service/PendingCall.java b/src/main/java/org/red5/server/service/PendingCall.java deleted file mode 100644 index 0c5607fa5..000000000 --- a/src/main/java/org/red5/server/service/PendingCall.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.service; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; -import java.util.HashSet; -import java.util.Set; - -import org.red5.server.api.service.IPendingServiceCall; -import org.red5.server.api.service.IPendingServiceCallback; - -/** - * Pending call is remote call operation that is in pending state. Remote calls to services - * are asynchronous, that is, after call but before result callback remote calls are in - * pending state. - */ -public class PendingCall extends Call implements IPendingServiceCall { - - private static final long serialVersionUID = 3219267601240355335L; - - /** - * Result object - */ - private Object result; - - /** - * List of callbacks (event listeners) - */ - private HashSet callbacks = new HashSet(); - - public PendingCall() { - } - - /** - * Creates pending call with given method name - * @param method Method name - */ - public PendingCall(String method) { - super(method); - } - - /** - * Creates pending call with given method name and array of parameters - * @param method Method name - * @param args Parameters - */ - public PendingCall(String method, Object[] args) { - super(method, args); - } - - /** - * Creates pending call with given method name, service name and array of parameters - * - * @param name Service name - * @param method Method name - * @param args Parameters - */ - public PendingCall(String name, String method, Object[] args) { - super(name, method, args); - } - - /** {@inheritDoc} - */ - public Object getResult() { - return result; - } - - /** {@inheritDoc} - */ - public void setResult(Object result) { - this.result = result; - setStatus(result == null ? STATUS_SUCCESS_NULL : STATUS_SUCCESS_RESULT); - } - - /** {@inheritDoc} */ - public void registerCallback(IPendingServiceCallback callback) { - callbacks.add(callback); - } - - /** {@inheritDoc} */ - public void unregisterCallback(IPendingServiceCallback callback) { - callbacks.remove(callback); - } - - /** {@inheritDoc} */ - public Set getCallbacks() { - return callbacks; - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - result = in.readObject(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - out.writeObject(result); - } -} diff --git a/src/main/java/org/red5/server/service/ReflectionUtils.java b/src/main/java/org/red5/server/service/ReflectionUtils.java deleted file mode 100644 index 2ca892ce1..000000000 --- a/src/main/java/org/red5/server/service/ReflectionUtils.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.service; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import org.red5.io.utils.ConversionUtils; -import org.red5.server.api.IConnection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Provides a means for locating methods within service classes using reflection. - */ -public class ReflectionUtils { - - private static final Logger log = LoggerFactory.getLogger(ReflectionUtils.class); - - //used to prevent extra object creation when a method with a set of params is not found - private static final Object[] nullReturn = new Object[] { null, null }; - - /** - * Returns (method, params) for the given service or (null, null) if no - * method was found. - * - * @param service Service - * @param methodName Method name - * @param args Arguments - * @return Method/params pairs - */ - public static Object[] findMethodWithExactParameters(Object service, String methodName, List args) { - Object[] arguments = new Object[args.size()]; - for (int i = 0; i < args.size(); i++) { - arguments[i] = args.get(i); - } - return findMethodWithExactParameters(service, methodName, arguments); - } - - /** - * Returns (method, params) for the given service or (null, null) if not - * method was found. XXX use ranking for method matching rather than exact - * type matching plus type conversion. - * - * @param service Service - * @param methodName Method name - * @param args Arguments - * @return Method/params pairs - */ - public static Object[] findMethodWithExactParameters(Object service, String methodName, Object[] args) { - int numParams = (args == null) ? 0 : args.length; - Method method = null; - try { - //try to skip the listing of all the methods by checking for exactly what we want first - Class[] params = ConversionUtils.convertParams(args); - if (log.isDebugEnabled()) { - for (Class clazz : params) { - log.debug("Parameter: {}", clazz); - } - } - method = service.getClass().getMethod(methodName, params); - log.debug("Exact method found (skipping list): {}", methodName); - return new Object[] { method, args }; - } catch (NoSuchMethodException nsme) { - log.debug("Method not found using exact parameter types"); - } - List methods = ConversionUtils.findMethodsByNameAndNumParams(service, methodName, numParams); - log.debug("Found {} methods", methods.size()); - if (methods.isEmpty()) { - return new Object[] { null, null }; - } else if (methods.size() == 1 && args == null) { - return new Object[] { methods.get(0), null }; - } else if (methods.size() > 1) { - log.debug("Multiple methods found with same name and parameter count; parameter conversion will be attempted in order."); - } - Object[] params = null; - // search for method with exact parameters - for (int i = 0; i < methods.size(); i++) { - method = methods.get(i); - boolean valid = true; - Class[] paramTypes = method.getParameterTypes(); - for (int j = 0; j < args.length; j++) { - if ((args[j] == null && paramTypes[j].isPrimitive()) || (args[j] != null && !args[j].getClass().equals(paramTypes[j]))) { - valid = false; - break; - } - } - if (valid) { - return new Object[] { method, args }; - } - } - // try to convert parameters - for (int i = 0; i < methods.size(); i++) { - try { - method = methods.get(i); - params = ConversionUtils.convertParams(args, method.getParameterTypes()); - if (args.length > 0 && (args[0] instanceof IConnection) && (!(params[0] instanceof IConnection))) { - // don't convert first IConnection parameter - continue; - } - return new Object[] { method, params }; - } catch (Exception ex) { - log.debug("Parameter conversion failed for {}", method); - } - } - return new Object[] { null, null }; - } - - /** - * Returns (method, params) for the given service or (null, null) if no - * method was found. - * - * @param service Service - * @param methodName Method name - * @param args Arguments - * @return Method/params pairs - */ - public static Object[] findMethodWithListParameters(Object service, String methodName, List args) { - Object[] arguments = new Object[args.size()]; - for (int i = 0; i < args.size(); i++) { - arguments[i] = args.get(i); - } - return findMethodWithListParameters(service, methodName, arguments); - } - - /** - * Returns (method, params) for the given service or (null, null) if not - * method was found. - * - * @param service Service - * @param methodName Method name - * @param args Arguments - * @return Method/params pairs - */ - public static Object[] findMethodWithListParameters(Object service, String methodName, Object[] args) { - Method method = null; - try { - //try to skip the listing of all the methods by checking for exactly what - //we want first - method = service.getClass().getMethod(methodName, ConversionUtils.convertParams(args)); - log.debug("Exact method found (skipping list): {}", methodName); - return new Object[] { method, args }; - } catch (NoSuchMethodException nsme) { - log.debug("Method not found using exact parameter types"); - } - List methods = ConversionUtils.findMethodsByNameAndNumParams(service, methodName, 1); - log.debug("Found {} methods", methods.size()); - if (methods.isEmpty()) { - return new Object[] { null, null }; - } else if (methods.size() > 1) { - log.debug("Multiple methods found with same name and parameter count; parameter conversion will be attempted in order."); - } - ArrayList argsList = new ArrayList(); - if (args != null) { - for (Object element : args) { - argsList.add(element); - } - } - args = new Object[] { argsList }; - Object[] params = null; - for (int i = 0; i < methods.size(); i++) { - try { - method = methods.get(i); - params = ConversionUtils.convertParams(args, method.getParameterTypes()); - if (argsList.size() > 0 && (argsList.get(0) instanceof IConnection) && (!(params[0] instanceof IConnection))) { - // Don't convert first IConnection parameter - continue; - } - return new Object[] { method, params }; - } catch (Exception ex) { - log.debug("Parameter conversion failed", ex); - } - } - return nullReturn; - } - -} diff --git a/src/main/java/org/red5/server/service/ServiceInvoker.java b/src/main/java/org/red5/server/service/ServiceInvoker.java deleted file mode 100644 index a22179084..000000000 --- a/src/main/java/org/red5/server/service/ServiceInvoker.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.service; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.red5.annotations.DeclarePrivate; -import org.red5.annotations.DeclareProtected; -import org.red5.server.api.IConnection; -import org.red5.server.api.Red5; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.service.IPendingServiceCall; -import org.red5.server.api.service.IServiceCall; -import org.red5.server.api.service.IServiceInvoker; -import org.red5.server.exception.ClientDetailsException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Makes remote calls, invoking services, resolves service handlers - * - * @author The Red5 Project - * @author Luke Hubbard, Codegent Ltd (luke@codegent.com) - */ -public class ServiceInvoker implements IServiceInvoker { - - /** - * Logger - */ - private static final Logger log = LoggerFactory.getLogger(ServiceInvoker.class); - - /** - * Service name - */ - public static final String SERVICE_NAME = "serviceInvoker"; - - /** - * Service resolvers set - */ - private Set serviceResolvers = new HashSet(); - - /** - * Setter for service resolvers. - * - * @param resolvers Service resolvers - */ - public void setServiceResolvers(Set resolvers) { - serviceResolvers = resolvers; - } - - /** - * Lookup a handler for the passed service name in the given scope. - * - * @param scope - * Scope - * @param serviceName - * Service name - * @return Service handler - */ - private Object getServiceHandler(IScope scope, String serviceName) { - // Get application scope handler first - Object service = scope.getHandler(); - if (serviceName == null || serviceName.equals("")) { - // No service requested, return application scope handler - return service; - } - // Search service resolver that knows about service name - for (IServiceResolver resolver : serviceResolvers) { - service = resolver.resolveService(scope, serviceName); - if (service != null) { - return service; - } - } - // Requested service does not exist. - return null; - } - - /** {@inheritDoc} */ - public boolean invoke(IServiceCall call, IScope scope) { - String serviceName = call.getServiceName(); - log.trace("Service name {}", serviceName); - Object service = getServiceHandler(scope, serviceName); - if (service == null) { - // Exception must be thrown if service was not found - call.setException(new ServiceNotFoundException(serviceName)); - // Set call status - call.setStatus(Call.STATUS_SERVICE_NOT_FOUND); - log.warn("Service not found: {}", serviceName); - return false; - } else { - log.trace("Service found: {}", serviceName); - } - // Invoke if everything is ok - return invoke(call, service); - } - - /** {@inheritDoc} */ - public boolean invoke(IServiceCall call, Object service) { - IConnection conn = Red5.getConnectionLocal(); - String methodName = call.getServiceMethodName(); - log.debug("Service: {} name: {} method: {}", new Object[] { service, call.getServiceName(), methodName }); - // pull off the prefixes since java doesnt allow this on a method name - if (methodName.charAt(0) == '@') { - log.debug("Method name contained an illegal prefix, it will be removed: {}", methodName); - methodName = methodName.substring(1); - } - // build an array with the incoming args and the current connection as the first element - Object[] args = call.getArguments(); - Object[] argsWithConnection; - if (args != null) { - argsWithConnection = new Object[args.length + 1]; - argsWithConnection[0] = conn; - for (int i = 0; i < args.length; i++) { - log.debug("{} => {}", i, args[i]); - if (args[i] != null) { - log.trace("Arg type: {}", args[i].getClass().getName()); - } - argsWithConnection[i + 1] = args[i]; - } - } else { - argsWithConnection = new Object[] { conn }; - } - // find the method - Object[] methodResult = null; - // First, search for method with the connection as first parameter. - methodResult = ReflectionUtils.findMethodWithExactParameters(service, methodName, argsWithConnection); - if (methodResult.length == 0 || methodResult[0] == null) { - // Second, search for method without the connection as first parameter. - methodResult = ReflectionUtils.findMethodWithExactParameters(service, methodName, args); - if (methodResult.length == 0 || methodResult[0] == null) { - // Third, search for method with the connection as first parameter in a list argument. - methodResult = ReflectionUtils.findMethodWithListParameters(service, methodName, argsWithConnection); - if (methodResult.length == 0 || methodResult[0] == null) { - // Fourth, search for method without the connection as first parameter in a list argument. - methodResult = ReflectionUtils.findMethodWithListParameters(service, methodName, args); - if (methodResult.length == 0 || methodResult[0] == null) { - log.error("Method {} with parameters {} not found in {}", - new Object[] { methodName, (args == null ? Collections.EMPTY_LIST : Arrays.asList(args)), service }); - call.setStatus(Call.STATUS_METHOD_NOT_FOUND); - if (args != null && args.length > 0) { - call.setException(new MethodNotFoundException(methodName, args)); - } else { - call.setException(new MethodNotFoundException(methodName)); - } - return false; - } - } - } - } - - Object result = null; - Method method = (Method) methodResult[0]; - Object[] params = (Object[]) methodResult[1]; - - try { - if (method.isAnnotationPresent(DeclarePrivate.class)) { - // Method may not be called by clients. - log.debug("Method {} is declared private.", method); - throw new NotAllowedException("Access denied, method is private"); - } - final DeclareProtected annotation = method.getAnnotation(DeclareProtected.class); - if (annotation != null) { - if (!conn.getClient().hasPermission(conn, annotation.permission())) { - // client doesn't have required permission - log.debug("Client {} doesn't have required permission {} to call {}", new Object[] { conn.getClient(), annotation.permission(), method }); - throw new NotAllowedException("Access denied, method is protected"); - } - } - log.debug("Invoking method: {}", method.toString()); - if (method.getReturnType().equals(Void.TYPE)) { - log.debug("result: void"); - method.invoke(service, params); - call.setStatus(Call.STATUS_SUCCESS_VOID); - } else { - result = method.invoke(service, params); - log.debug("result: {}", result); - call.setStatus(result == null ? Call.STATUS_SUCCESS_NULL : Call.STATUS_SUCCESS_RESULT); - } - if (call instanceof IPendingServiceCall) { - ((IPendingServiceCall) call).setResult(result); - } - } catch (NotAllowedException e) { - call.setException(e); - call.setStatus(Call.STATUS_ACCESS_DENIED); - return false; - } catch (IllegalAccessException accessEx) { - call.setException(accessEx); - call.setStatus(Call.STATUS_ACCESS_DENIED); - log.error("Error executing call: {}", call); - log.error("Service invocation error", accessEx); - return false; - } catch (InvocationTargetException invocationEx) { - call.setException(invocationEx); - call.setStatus(Call.STATUS_INVOCATION_EXCEPTION); - if (!(invocationEx.getCause() instanceof ClientDetailsException)) { - // only log if not handled by client - log.error("Error executing call: {}", call, invocationEx); - } - return false; - } catch (Exception ex) { - call.setException(ex); - call.setStatus(Call.STATUS_GENERAL_EXCEPTION); - log.error("Error executing call: {}", call, ex); - return false; - } - return true; - } - -} diff --git a/src/main/java/org/red5/server/service/ServiceNotFoundException.java b/src/main/java/org/red5/server/service/ServiceNotFoundException.java deleted file mode 100644 index 344d3f840..000000000 --- a/src/main/java/org/red5/server/service/ServiceNotFoundException.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.service; - -/** - * Thrown when service can't be found thus remote call throws an exception - * - * @author The Red5 Project - * @author Luke Hubbard, Codegent Ltd (luke@codegent.com) - */ -public class ServiceNotFoundException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = 7543755414829244027L; - - /** Name of service that doesn't exist. */ - private String serviceName; - - /** - * Creates new exception with service name - * @param serviceName Name of service that couldn't been found - */ - public ServiceNotFoundException(String serviceName) { - super("Service not found: " + serviceName); - this.serviceName = serviceName; - } - - /** - * Get the name of the service that doesn't exist. - * - * @return name of the service - */ - public String getServiceName() { - return serviceName; - } - -} diff --git a/src/main/java/org/red5/server/so/ClientSharedObject.java b/src/main/java/org/red5/server/so/ClientSharedObject.java deleted file mode 100644 index c1277eaaf..000000000 --- a/src/main/java/org/red5/server/so/ClientSharedObject.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.so; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.locks.ReentrantLock; - -import org.red5.server.api.IAttributeStore; -import org.red5.server.api.IConnection; -import org.red5.server.api.event.IEvent; -import org.red5.server.api.event.IEventDispatcher; -import org.red5.server.api.event.IEventListener; -import org.red5.server.api.so.IClientSharedObject; -import org.red5.server.api.so.ISharedObjectListener; -import org.red5.server.net.rtmp.Channel; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.so.ISharedObjectEvent.Type; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Works with client-side shared object - */ -@SuppressWarnings("unchecked") -public class ClientSharedObject extends SharedObject implements IClientSharedObject, IEventDispatcher { - - protected static Logger log = LoggerFactory.getLogger(ClientSharedObject.class); - - /** - * Initial synchronization flag - */ - private volatile boolean initialSyncReceived; - - /** - * Synchronization lock - */ - private final ReentrantLock lock = new ReentrantLock(); - - /** - * Set of listeners - */ - private Set listeners = new CopyOnWriteArraySet(); - - /** - * Set of event handlers - */ - private ConcurrentMap handlers = new ConcurrentHashMap(1, 0.9f, 1); - - /** - * Create new client SO with - * - * @param name Shared Object name - * @param persistent Persistence flag - */ - public ClientSharedObject(String name, boolean persistent) { - super(); - this.name = name; - this.persistent = persistent; - } - - /** - * Connect the shared object using the passed connection. - * - * @param conn Attach SO to given connection - */ - public void connect(IConnection conn) { - if (conn instanceof RTMPConnection) { - if (!isConnected()) { - source = conn; - SharedObjectMessage msg = new SharedObjectMessage(name, 0, isPersistent()); - msg.addEvent(new SharedObjectEvent(Type.SERVER_CONNECT, null, null)); - Channel c = ((RTMPConnection) conn).getChannel((byte) 3); - c.write(msg); - } else { - throw new UnsupportedOperationException("Already connected"); - } - } else { - throw new UnsupportedOperationException("Only RTMP connections are supported"); - } - } - - /** {@inheritDoc} */ - public void disconnect() { - if (isConnected()) { - SharedObjectMessage msg = new SharedObjectMessage(name, 0, isPersistent()); - msg.addEvent(new SharedObjectEvent(Type.SERVER_DISCONNECT, null, null)); - Channel c = ((RTMPConnection) source).getChannel((byte) 3); - c.write(msg); - notifyDisconnect(); - initialSyncReceived = false; - } - } - - /** {@inheritDoc} */ - public boolean isConnected() { - return initialSyncReceived; - } - - /** {@inheritDoc} */ - public void addSharedObjectListener(ISharedObjectListener listener) { - listeners.add(listener); - } - - /** {@inheritDoc} */ - public void removeSharedObjectListener(ISharedObjectListener listener) { - listeners.remove(listener); - } - - /** {@inheritDoc} */ - public void dispatchEvent(IEvent e) { - if (e instanceof ISharedObjectMessage || e.getType() == IEvent.Type.SHARED_OBJECT) { - ISharedObjectMessage msg = (ISharedObjectMessage) e; - if (msg.hasSource()) { - beginUpdate(msg.getSource()); - } else { - beginUpdate(); - } - try { - for (ISharedObjectEvent event : msg.getEvents()) { - switch (event.getType()) { - case CLIENT_INITIAL_DATA: - initialSyncReceived = true; - notifyConnect(); - break; - case CLIENT_CLEAR_DATA: - attributes.clear(); - notifyClear(); - break; - case CLIENT_DELETE_DATA: - case CLIENT_DELETE_ATTRIBUTE: - attributes.remove(event.getKey()); - notifyDelete(event.getKey()); - break; - case CLIENT_SEND_MESSAGE: - notifySendMessage(event.getKey(), (List) event.getValue()); - break; - case CLIENT_UPDATE_DATA: - attributes.putAll((Map) event.getValue()); - notifyUpdate(event.getKey(), (Map) event.getValue()); - break; - case CLIENT_UPDATE_ATTRIBUTE: - Object val = event.getValue(); - // null values are not allowed in concurrent hash maps - if (val != null) { - attributes.put(event.getKey(), val); - } - // we will however send the null out to the subscribers - notifyUpdate(event.getKey(), val); - break; - default: - log.warn("Unknown SO event: {}", event.getType()); - } - } - } finally { - endUpdate(); - } - } - } - - /** - * Notify listeners on event - */ - protected void notifyConnect() { - for (ISharedObjectListener listener : listeners) { - listener.onSharedObjectConnect(this); - } - } - - /** - * Notify listeners on disconnect - */ - protected void notifyDisconnect() { - for (ISharedObjectListener listener : listeners) { - listener.onSharedObjectDisconnect(this); - } - } - - /** - * Notify listeners on update - * - * @param key - * Updated attribute key - * @param value - * Updated attribute value - */ - protected void notifyUpdate(String key, Object value) { - for (ISharedObjectListener listener : listeners) { - listener.onSharedObjectUpdate(this, key, value); - } - } - - /** - * Notify listeners on map attribute update - * - * @param key - * Updated attribute key - * @param value - * Updated attribute value - */ - protected void notifyUpdate(String key, Map value) { - if (value.size() == 1) { - Map.Entry entry = value.entrySet().iterator().next(); - notifyUpdate(entry.getKey(), entry.getValue()); - return; - } - for (ISharedObjectListener listener : listeners) { - listener.onSharedObjectUpdate(this, key, value); - } - } - - /** - * Notify listeners on attribute delete - * - * @param key - * Attribute name - */ - protected void notifyDelete(String key) { - for (ISharedObjectListener listener : listeners) { - listener.onSharedObjectDelete(this, key); - } - } - - /** - * Notify listeners on clear - */ - protected void notifyClear() { - for (ISharedObjectListener listener : listeners) { - listener.onSharedObjectClear(this); - } - } - - /** - * Broadcast send event to listeners - * - * @param method - * Method name - * @param params - * Params - */ - protected void notifySendMessage(String method, List params) { - for (ISharedObjectListener listener : listeners) { - listener.onSharedObjectSend(this, method, params); - } - } - - /** {@inheritDoc} */ - @Override - public boolean setAttribute(String name, Object value) { - if (value != null) { - ownerMessage.addEvent(Type.SERVER_SET_ATTRIBUTE, name, value); - notifyModified(); - return true; - } else { - return removeAttribute(name); - } - } - - /** {@inheritDoc} */ - @Override - public boolean setAttributes(IAttributeStore values) { - return setAttributes(values.getAttributes()); - } - - /** {@inheritDoc} */ - @Override - public boolean setAttributes(Map values) { - int successes = 0; - if (values != null) { - for (Map.Entry entry : values.entrySet()) { - if (setAttribute(entry.getKey(), entry.getValue())) { - successes++; - } - } - } - // expect every value to have been added - return (successes == values.size()); - } - - /** {@inheritDoc} */ - @Override - public boolean removeAttribute(String name) { - ownerMessage.addEvent(Type.SERVER_DELETE_ATTRIBUTE, name, null); - notifyModified(); - return true; - } - - /** {@inheritDoc} */ - @Override - public void sendMessage(String handler, List arguments) { - ownerMessage.addEvent(Type.SERVER_SEND_MESSAGE, handler, arguments); - notifyModified(); - } - - /** {@inheritDoc} */ - @Override - public void removeAttributes() { - // TODO: there must be a direct way to clear the SO on the client side - for (String key : getAttributeNames()) { - ownerMessage.addEvent(Type.SERVER_DELETE_ATTRIBUTE, key, null); - } - notifyModified(); - } - - /** {@inheritDoc} */ - @Override - public boolean clear() { - return super.clear(); - } - - /** {@inheritDoc} */ - @Override - public void close() { - super.close(); - } - - /** {@inheritDoc} */ - @Override - public void beginUpdate() { - lock(); - super.beginUpdate(); - } - - /** {@inheritDoc} */ - @Override - public void beginUpdate(IEventListener listener) { - lock(); - super.beginUpdate(listener); - } - - /** {@inheritDoc} */ - @Override - public void endUpdate() { - super.endUpdate(); - unlock(); - } - - /** {@inheritDoc} */ - public void lock() { - lock.lock(); - } - - /** {@inheritDoc} */ - public void unlock() { - lock.unlock(); - } - - /** {@inheritDoc} */ - public boolean isLocked() { - return lock.isLocked(); - } - - /** {@inheritDoc} */ - public void registerServiceHandler(Object handler) { - registerServiceHandler("", handler); - } - - /** {@inheritDoc} */ - public void unregisterServiceHandler(String name) { - handlers.remove(name); - } - - /** {@inheritDoc} */ - public void registerServiceHandler(String name, Object handler) { - if (name == null) { - name = ""; - } - handlers.put(name, handler); - } - - /** {@inheritDoc} */ - public Object getServiceHandler(String name) { - if (name == null) { - name = ""; - } - return handlers.get(name); - } - - /** {@inheritDoc} */ - public Set getServiceHandlerNames() { - return Collections.unmodifiableSet(handlers.keySet()); - } - - /** {@inheritDoc} */ - public Object getAttribute(String name, Object defaultValue) { - if (!hasAttribute(name)) { - setAttribute(name, defaultValue); - } - return getAttribute(name); - } - -} diff --git a/src/main/java/org/red5/server/so/FlexSharedObjectMessage.java b/src/main/java/org/red5/server/so/FlexSharedObjectMessage.java deleted file mode 100644 index 10276b598..000000000 --- a/src/main/java/org/red5/server/so/FlexSharedObjectMessage.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.so; - -import org.red5.server.api.event.IEventListener; - -public class FlexSharedObjectMessage extends SharedObjectMessage { - - private static final long serialVersionUID = -6458750398936033347L; - - public FlexSharedObjectMessage() { - } - - /** - * Creates Flex Shared Object event with given name, version and persistence flag - * - * @param name Event name - * @param version SO version - * @param persistent SO persistence flag - */ - public FlexSharedObjectMessage(String name, int version, boolean persistent) { - this(null, name, version, persistent); - } - - /** - * Creates Flex Shared Object event with given listener, name, SO version and persistence flag - * - * @param source Event listener - * @param name Event name - * @param version SO version - * @param persistent SO persistence flag - */ - public FlexSharedObjectMessage(IEventListener source, String name, int version, boolean persistent) { - super(source, name, version, persistent); - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return TYPE_FLEX_SHARED_OBJECT; - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/so/ISharedObjectEvent.java b/src/main/java/org/red5/server/so/ISharedObjectEvent.java deleted file mode 100644 index 1f0ea3633..000000000 --- a/src/main/java/org/red5/server/so/ISharedObjectEvent.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.so; - -/** - * One update event for a shared object received through a connection. - */ -public interface ISharedObjectEvent { - - enum Type { - SERVER_CONNECT, SERVER_DISCONNECT, SERVER_SET_ATTRIBUTE, SERVER_DELETE_ATTRIBUTE, - SERVER_SEND_MESSAGE, CLIENT_CLEAR_DATA, CLIENT_DELETE_ATTRIBUTE, CLIENT_DELETE_DATA, - CLIENT_INITIAL_DATA, CLIENT_STATUS, CLIENT_UPDATE_DATA, CLIENT_UPDATE_ATTRIBUTE, - CLIENT_SEND_MESSAGE - }; - - /** - * Returns the type of the event. - * - * @return the type of the event. - */ - public Type getType(); - - /** - * Returns the key of the event. - * - * Depending on the type this contains: - *
    - *
  • the attribute name to set for SET_ATTRIBUTE
  • - *
  • the attribute name to delete for DELETE_ATTRIBUTE
  • - *
  • the handler name to call for SEND_MESSAGE
  • - *
- * In all other cases the key is null. - * - * @return the key of the event - */ - public String getKey(); - - /** - * Returns the value of the event. - * - * Depending on the type this contains: - *
    - *
  • the attribute value to set for SET_ATTRIBUTE
  • - *
  • a list of parameters to pass to the handler for SEND_MESSAGE
  • - *
- * In all other cases the value is null. - * - * @return the value of the event - */ - public Object getValue(); -} diff --git a/src/main/java/org/red5/server/so/ISharedObjectMessage.java b/src/main/java/org/red5/server/so/ISharedObjectMessage.java deleted file mode 100644 index 21fe58ea4..000000000 --- a/src/main/java/org/red5/server/so/ISharedObjectMessage.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.so; - -import java.util.Queue; - -import org.red5.server.net.rtmp.event.IRTMPEvent; - -/** - * Shared object message - */ -public interface ISharedObjectMessage extends IRTMPEvent { - - /** - * Returns the name of the shared object this message belongs to. - * - * @return name of the shared object - */ - public String getName(); - - /** - * Returns the version to modify. - * - * @return version to modify - */ - public int getVersion(); - - /** - * Does the message affect a persistent shared object? - * - * @return true if a persistent shared object should be updated otherwise - * false - */ - public boolean isPersistent(); - - /** - * Returns a set of ISharedObjectEvent objects containing informations what - * to change. - * - * @return set of ISharedObjectEvents - */ - public Queue getEvents(); - - /** - * Addition event handler - * @param type Event type - * @param key Handler key - * @param value Event value (like arguments) - * @return true if event is added and false if it is not added - */ - public boolean addEvent(ISharedObjectEvent.Type type, String key, Object value); - - /** - * Add event handler - * @param event SO event - */ - public void addEvent(ISharedObjectEvent event); - - /** - * Clear shared object - */ - public void clear(); - - /** - * Is empty? - * - * @return true if shared object is empty, false otherwise - */ - public boolean isEmpty(); - -} diff --git a/src/main/java/org/red5/server/so/SharedObject.java b/src/main/java/org/red5/server/so/SharedObject.java deleted file mode 100644 index 339913c9a..000000000 --- a/src/main/java/org/red5/server/so/SharedObject.java +++ /dev/null @@ -1,796 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.so; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.red5.io.object.Deserializer; -import org.red5.io.object.Input; -import org.red5.io.object.Output; -import org.red5.io.object.Serializer; -import org.red5.server.AttributeStore; -import org.red5.server.api.IAttributeStore; -import org.red5.server.api.Red5; -import org.red5.server.api.event.IEventListener; -import org.red5.server.api.persistence.IPersistable; -import org.red5.server.api.persistence.IPersistenceStore; -import org.red5.server.api.scope.ScopeType; -import org.red5.server.api.statistics.ISharedObjectStatistics; -import org.red5.server.api.statistics.support.StatisticsCounter; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.net.rtmp.codec.RTMP; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.so.ISharedObjectEvent.Type; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Represents shared object on server-side. Shared Objects in Flash are like cookies that are stored - * on client side. In Red5 and Flash Media Server there's one more special type of SOs : remote Shared Objects. - * - * These are shared by multiple clients and synchronized between them automatically on each data change. This is done - * asynchronously, used as events handling and is widely used in multiplayer Flash online games. - * - * Shared object can be persistent or transient. The difference is that first are saved to the disk and can be - * accessed later on next connection, transient objects are not saved and get lost each time they last client - * disconnects from it. - * - * Shared Objects has name identifiers and path on server's HD (if persistent). On deeper level server-side - * Shared Object in this implementation actually uses IPersistenceStore to delegate all (de)serialization work. - * - * SOs store data as simple map, that is, "name-value" pairs. Each value in turn can be complex object or map. - * - * All access to methods that change properties in the SO must be properly - * synchronized for multi-threaded access. - */ -public class SharedObject extends AttributeStore implements ISharedObjectStatistics, IPersistable, Constants { - - protected static Logger log = LoggerFactory.getLogger(SharedObject.class); - - /** - * Shared Object name (identifier) - */ - protected String name = ""; - - /** - * SO path - */ - protected String path = ""; - - /** - * true if the SharedObject was stored by the persistence framework and can be used later on reconnection - */ - protected boolean persistent; - - /** - * Object that is delegated with all storage work for persistent SOs - */ - protected IPersistenceStore storage; - - /** - * Version. Used on synchronization purposes. - */ - protected volatile AtomicInteger version = new AtomicInteger(1); - - /** - * Number of pending update operations - */ - protected volatile AtomicInteger updateCounter = new AtomicInteger(); - - /** - * Has changes? flag - */ - protected volatile boolean modified; - - /** - * Last modified timestamp - */ - protected long lastModified = -1; - - /** - * Owner event - */ - protected SharedObjectMessage ownerMessage; - - /** - * Synchronization events - */ - protected volatile ConcurrentLinkedQueue syncEvents = new ConcurrentLinkedQueue(); - - /** - * Listeners - */ - protected volatile CopyOnWriteArraySet listeners = new CopyOnWriteArraySet(); - - /** - * Event listener, actually RTMP connection - */ - protected IEventListener source; - - /** - * Number of times the SO has been acquired - */ - protected volatile AtomicInteger acquireCount = new AtomicInteger(); - - /** - * Timestamp the scope was created. - */ - private long creationTime; - - /** - * Manages listener statistics. - */ - protected StatisticsCounter listenerStats = new StatisticsCounter(); - - /** - * Counts number of "change" events. - */ - protected AtomicInteger changeStats = new AtomicInteger(); - - /** - * Counts number of "delete" events. - */ - protected AtomicInteger deleteStats = new AtomicInteger(); - - /** - * Counts number of "send message" events. - */ - protected AtomicInteger sendStats = new AtomicInteger(); - - /** - * Whether or not this shared object is closed - */ - protected volatile AtomicBoolean closed = new AtomicBoolean(false); - - /** Constructs a new SharedObject. */ - public SharedObject() { - // This is used by the persistence framework - super(); - ownerMessage = new SharedObjectMessage(null, null, -1, false); - creationTime = System.currentTimeMillis(); - } - - /** - * Constructs new SO from Input object - * @param input Input source - * @throws IOException I/O exception - * - * @see org.red5.io.object.Input - */ - public SharedObject(Input input) throws IOException { - this(); - deserialize(input); - } - - /** - * Creates new SO from given data map, name, path and persistence option - * - * @param name SO name - * @param path SO path - * @param persistent SO persistence - */ - public SharedObject(String name, String path, boolean persistent) { - super(); - this.name = name; - this.path = path; - this.persistent = persistent; - ownerMessage = new SharedObjectMessage(null, name, 0, persistent); - creationTime = System.currentTimeMillis(); - } - - /** - * Creates new SO from given data map, name, path, storage object and persistence option - * - * @param name SO name - * @param path SO path - * @param persistent SO persistence - * @param storage Persistence storage - */ - public SharedObject(String name, String path, boolean persistent, IPersistenceStore storage) { - this(name, path, persistent); - setStore(storage); - } - - /** - * Creates new SO from given data map, name, path and persistence option - * - * @param data Data - * @param name SO name - * @param path SO path - * @param persistent SO persistence - */ - public SharedObject(Map data, String name, String path, boolean persistent) { - this(name, path, persistent); - attributes.putAll(data); - } - - /** - * Creates new SO from given data map, name, path, storage object and persistence option - * - * @param data Data - * @param name SO name - * @param path SO path - * @param persistent SO persistence - * @param storage Persistence storage - */ - public SharedObject(Map data, String name, String path, boolean persistent, IPersistenceStore storage) { - this(data, name, path, persistent); - setStore(storage); - } - - /** {@inheritDoc} */ - public String getName() { - return name; - } - - /** {@inheritDoc} */ - public void setName(String name) { - throw new UnsupportedOperationException(String.format("Name change not supported; current name: %s", getName())); - } - - /** {@inheritDoc} */ - public String getPath() { - return path; - } - - /** {@inheritDoc} */ - public void setPath(String path) { - this.path = path; - } - - /** {@inheritDoc} */ - public String getType() { - return ScopeType.SHARED_OBJECT.toString(); - } - - /** {@inheritDoc} */ - public long getLastModified() { - return lastModified; - } - - /** {@inheritDoc} */ - public boolean isPersistent() { - return persistent; - } - - /** {@inheritDoc} */ - public void setPersistent(boolean persistent) { - log.debug("setPersistent: {}", persistent); - this.persistent = persistent; - } - - /** - * Send update notification over data channel of RTMP connection - */ - protected void sendUpdates() { - log.debug("sendUpdates"); - // get the current version - final int currentVersion = getVersion(); - log.debug("Current version: {}", currentVersion); - // get the name - final String name = getName(); - //get owner events - ConcurrentLinkedQueue ownerEvents = ownerMessage.getEvents(); - if (!ownerEvents.isEmpty()) { - // get all current owner events - final ConcurrentLinkedQueue events = new ConcurrentLinkedQueue(); - if (ownerEvents.size() > SharedObjectService.MAXIMUM_EVENTS_PER_UPDATE) { - log.debug("Owner events exceed max: {}", ownerEvents.size()); - for (int i = 0; i < SharedObjectService.MAXIMUM_EVENTS_PER_UPDATE; i++) { - events.add(ownerEvents.poll()); - } - } else { - events.addAll(ownerEvents); - ownerEvents.removeAll(events); - } - // send update to "owner" of this update request - if (source != null) { - final RTMPConnection con = (RTMPConnection) source; - // create a worker - SharedObjectService.submitTask(new Runnable() { - public void run() { - Red5.setConnectionLocal(con); - con.sendSharedObjectMessage(name, currentVersion, persistent, events); - Red5.setConnectionLocal(null); - } - }); - } - } - // tell all the listeners - if (!syncEvents.isEmpty()) { - // get all current sync events - final ConcurrentLinkedQueue events = new ConcurrentLinkedQueue(); - if (syncEvents.size() > SharedObjectService.MAXIMUM_EVENTS_PER_UPDATE) { - log.debug("Sync events exceed max: {}", syncEvents.size()); - for (int i = 0; i < SharedObjectService.MAXIMUM_EVENTS_PER_UPDATE; i++) { - events.add(syncEvents.poll()); - } - } else { - events.addAll(syncEvents); - syncEvents.removeAll(events); - } - // get the listeners - Set listeners = getListeners(); - if (log.isDebugEnabled()) { - log.debug("Listeners: {}", listeners); - } - // updates all registered clients of this shared object - for (IEventListener listener : listeners) { - if (listener != source) { - if (listener instanceof RTMPConnection) { - final RTMPConnection con = (RTMPConnection) listener; - if (con.getStateCode() == RTMP.STATE_CONNECTED) { - // create a worker - SharedObjectService.submitTask(new Runnable() { - public void run() { - Red5.setConnectionLocal(con); - con.sendSharedObjectMessage(name, currentVersion, persistent, events); - Red5.setConnectionLocal(null); - } - }); - } else { - log.debug("Skipping unconnected connection"); - } - } else { - log.warn("Can't send sync message to unknown connection {}", listener); - } - } else { - // don't re-send update to active client - log.debug("Skipped {}", source); - } - } - } - } - - /** - * Send notification about modification of SO - */ - protected void notifyModified() { - log.debug("notifyModified"); - if (updateCounter.get() == 0) { - if (modified) { - // client sent at least one update -> increase version of SO - updateVersion(); - lastModified = System.currentTimeMillis(); - if (storage == null || !storage.save(this)) { - log.warn("Could not store shared object"); - } - } else { - log.debug("Not modified"); - } - sendUpdates(); - modified = false; - } else { - log.debug("Update counter: {}", updateCounter.get()); - } - } - - /** - * Return an error message to the client. - * - * @param message - */ - protected void returnError(String message) { - ownerMessage.addEvent(Type.CLIENT_STATUS, "error", message); - } - - /** - * Return an attribute value to the owner. - * - * @param name - */ - protected void returnAttributeValue(String name) { - ownerMessage.addEvent(Type.CLIENT_UPDATE_DATA, name, getAttribute(name)); - } - - /** - * Return attribute by name and set if it doesn't exist yet. - * @param name Attribute name - * @param value Value to set if attribute doesn't exist - * @return Attribute value - */ - @Override - public Object getAttribute(String name, Object value) { - log.debug("getAttribute - name: {} value: {}", name, value); - Object result = null; - if (name != null) { - result = attributes.putIfAbsent(name, value); - if (result == null) { - // no previous value - modified = true; - ownerMessage.addEvent(Type.CLIENT_UPDATE_DATA, name, value); - syncEvents.add(new SharedObjectEvent(Type.CLIENT_UPDATE_DATA, name, value)); - notifyModified(); - changeStats.incrementAndGet(); - result = value; - } - } - return result; - } - - /** {@inheritDoc} */ - @Override - public boolean setAttribute(String name, Object value) { - log.debug("setAttribute - name: {} value: {}", name, value); - boolean result = true; - ownerMessage.addEvent(Type.CLIENT_UPDATE_ATTRIBUTE, name, null); - if (value == null && super.removeAttribute(name)) { - // Setting a null value removes the attribute - modified = true; - syncEvents.add(new SharedObjectEvent(Type.CLIENT_DELETE_DATA, name, null)); - deleteStats.incrementAndGet(); - } else if (value != null && super.setAttribute(name, value)) { - // only sync if the attribute changed - modified = true; - syncEvents.add(new SharedObjectEvent(Type.CLIENT_UPDATE_DATA, name, value)); - changeStats.incrementAndGet(); - } else { - result = false; - } - notifyModified(); - return result; - } - - /** {@inheritDoc} */ - @Override - public boolean setAttributes(Map values) { - int successes = 0; - if (values != null) { - beginUpdate(); - try { - for (Map.Entry entry : values.entrySet()) { - if (setAttribute(entry.getKey(), entry.getValue())) { - successes++; - } - } - } finally { - endUpdate(); - } - } - // expect every value to have been added - return (successes == values.size()); - } - - /** {@inheritDoc} */ - @Override - public boolean setAttributes(IAttributeStore values) { - if (values != null) { - return setAttributes(values.getAttributes()); - } - return false; - } - - /** - * Removes attribute with given name - * @param name Attribute - * @return true if there's such an attribute and it was removed, false otherwise - */ - @Override - public boolean removeAttribute(String name) { - boolean result = true; - // Send confirmation to client - ownerMessage.addEvent(Type.CLIENT_DELETE_DATA, name, null); - if (super.removeAttribute(name)) { - modified = true; - syncEvents.add(new SharedObjectEvent(Type.CLIENT_DELETE_DATA, name, null)); - deleteStats.incrementAndGet(); - } else { - result = false; - } - notifyModified(); - return result; - } - - /** - * Broadcast event to event handler - * @param handler Event handler - * @param arguments Arguments - */ - protected void sendMessage(String handler, List arguments) { - if (ownerMessage.addEvent(Type.CLIENT_SEND_MESSAGE, handler, arguments)) { - syncEvents.add(new SharedObjectEvent(Type.CLIENT_SEND_MESSAGE, handler, arguments)); - sendStats.incrementAndGet(); - if (log.isTraceEnabled()) { - log.trace("Send message: {}", arguments); - } - } - } - - /** - * Getter for data. - * - * @return SO data as unmodifiable map - */ - public Map getData() { - return getAttributes(); - } - - /** - * Getter for version. - * - * @return SO version. - */ - public int getVersion() { - return version.get(); - } - - /** - * Increases version by one - */ - private void updateVersion() { - version.incrementAndGet(); - } - - /** - * Remove all attributes (clear Shared Object) - */ - @Override - public void removeAttributes() { - // TODO: there must be a direct way to clear the SO on the client side... - Set names = getAttributeNames(); - for (String key : names) { - ownerMessage.addEvent(Type.CLIENT_DELETE_DATA, key, null); - syncEvents.add(new SharedObjectEvent(Type.CLIENT_DELETE_DATA, key, null)); - } - deleteStats.addAndGet(names.size()); - // clear data - super.removeAttributes(); - // mark as modified - modified = true; - // broadcast 'modified' event - notifyModified(); - } - - /** - * Register event listener - * @param listener Event listener - * @return true if listener was added - */ - protected boolean register(IEventListener listener) { - log.debug("register - listener: {}", listener); - boolean registered = listeners.add(listener); - if (registered) { - listenerStats.increment(); - // prepare response for new client - ownerMessage.addEvent(Type.CLIENT_INITIAL_DATA, null, null); - if (!isPersistent()) { - ownerMessage.addEvent(Type.CLIENT_CLEAR_DATA, null, null); - } - if (!attributes.isEmpty()) { - ownerMessage.addEvent(new SharedObjectEvent(Type.CLIENT_UPDATE_DATA, null, getAttributes())); - } - // we call notifyModified here to send response if we're not in a beginUpdate block - notifyModified(); - } - return registered; - } - - /** - * Unregister event listener - * @param listener Event listener - */ - protected void unregister(IEventListener listener) { - log.debug("unregister - listener: {}", listener); - listeners.remove(listener); - listenerStats.decrement(); - } - - /** - * Check if shared object must be released. - */ - protected void checkRelease() { - if (!isPersistent() && listeners.isEmpty() && !isAcquired()) { - log.info("Deleting shared object {} because all clients disconnected and it is no longer acquired.", name); - if (storage != null) { - if (!storage.remove(this)) { - log.error("Could not remove shared object"); - } - } - close(); - } - } - - /** - * Get event listeners. - * - * @return Value for property 'listeners'. - */ - public Set getListeners() { - return Collections.unmodifiableSet(listeners); - } - - /** - * Begin update of this Shared Object. - * Increases number of pending update operations - */ - protected void beginUpdate() { - log.debug("beginUpdate"); - beginUpdate(source); - } - - /** - * Begin update of this Shared Object and setting listener - * @param listener Update with listener - */ - protected void beginUpdate(IEventListener listener) { - log.debug("beginUpdate - listener: {}", listener); - source = listener; - // increase number of pending updates - updateCounter.incrementAndGet(); - } - - /** - * End update of this Shared Object. Decreases number of pending update operations and - * broadcasts modified event if it is equal to zero (i.e. no more pending update operations). - */ - protected void endUpdate() { - log.debug("endUpdate"); - // decrease number of pending updates - if (updateCounter.decrementAndGet() == 0) { - notifyModified(); - source = null; - } - } - - /** {@inheritDoc} */ - public void serialize(Output output) throws IOException { - log.debug("serialize"); - log.trace("Name: {}", name); - Serializer.serialize(output, getName()); - Map map = getAttributes(); - log.trace("Attributes: {}", map); - Serializer.serialize(output, map); - } - - /** {@inheritDoc} */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void deserialize(Input input) throws IOException { - log.debug("deserialize"); - name = Deserializer.deserialize(input, String.class); - log.trace("Name: {}", name); - persistent = true; - Map map = Deserializer. deserialize(input, Map.class); - log.trace("Attributes: {}", map); - super.setAttributes(map); - ownerMessage.setName(name); - ownerMessage.setPersistent(persistent); - } - - /** {@inheritDoc} */ - public void setStore(IPersistenceStore store) { - this.storage = store; - } - - /** {@inheritDoc} */ - public IPersistenceStore getStore() { - return storage; - } - - /** - * Deletes all the attributes and sends a clear event to all listeners. The - * persistent data object is also removed from a persistent shared object. - * - * @return true on success, false otherwise - */ - protected boolean clear() { - log.debug("clear"); - super.removeAttributes(); - // send confirmation to client - ownerMessage.addEvent(Type.CLIENT_CLEAR_DATA, name, null); - notifyModified(); - changeStats.incrementAndGet(); - return true; - } - - /** - * Detaches a reference from this shared object, reset it's state, this will destroy the - * reference immediately. This is useful when you don't want to proxy a shared object any longer. - */ - protected void close() { - log.debug("close"); - closed.compareAndSet(false, true); - // clear collections - super.removeAttributes(); - listeners.clear(); - syncEvents.clear(); - ownerMessage.getEvents().clear(); - } - - /** - * Prevent shared object from being released. Each call to acquire - * must be paired with a call to release so the SO isn't held - * forever. This is only valid for non-persistent SOs. - */ - public void acquire() { - log.debug("acquire"); - acquireCount.incrementAndGet(); - } - - /** - * Check if shared object currently is acquired. - * - * @return true if the SO is acquired, otherwise false - */ - public boolean isAcquired() { - return acquireCount.get() > 0; - } - - /** - * Release previously acquired shared object. If the SO is non-persistent, - * no more clients are connected the SO isn't acquired any more, the data - * is released. - */ - public void release() { - log.debug("release"); - if (acquireCount.get() == 0) { - throw new RuntimeException("The shared object was not acquired before."); - } - if (acquireCount.decrementAndGet() == 0) { - checkRelease(); - } - } - - public boolean isClosed() { - return closed.get(); - } - - /** {@inheritDoc} */ - public long getCreationTime() { - return creationTime; - } - - /** {@inheritDoc} */ - public int getTotalListeners() { - return listenerStats.getTotal(); - } - - /** {@inheritDoc} */ - public int getMaxListeners() { - return listenerStats.getMax(); - } - - /** {@inheritDoc} */ - public int getActiveListeners() { - return listenerStats.getCurrent(); - } - - /** {@inheritDoc} */ - public int getTotalChanges() { - return changeStats.intValue(); - } - - /** {@inheritDoc} */ - public int getTotalDeletes() { - return deleteStats.intValue(); - } - - /** {@inheritDoc} */ - public int getTotalSends() { - return sendStats.intValue(); - } - -} diff --git a/src/main/java/org/red5/server/so/SharedObjectEvent.java b/src/main/java/org/red5/server/so/SharedObjectEvent.java deleted file mode 100644 index 23a4bd089..000000000 --- a/src/main/java/org/red5/server/so/SharedObjectEvent.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.so; - -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -public class SharedObjectEvent implements ISharedObjectEvent, Externalizable { - - private static final long serialVersionUID = -4129018814289863535L; - - /** - * Event type - */ - private Type type; - - /** - * Changed pair key - */ - private String key; - - /** - * Changed pair value - */ - private Object value; - - public SharedObjectEvent() { - } - - /** - * - * @param type type - * @param key key - * @param value value - */ - public SharedObjectEvent(Type type, String key, Object value) { - this.type = type; - this.key = key; - this.value = value; - } - - /** {@inheritDoc} */ - public String getKey() { - return key; - } - - /** {@inheritDoc} */ - public Type getType() { - return type; - } - - /** {@inheritDoc} */ - public Object getValue() { - return value; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SharedObjectEvent other = (SharedObjectEvent) obj; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (type != other.type) - return false; - if (value == null) { - if (other.value != null) - return false; - } else if (!value.equals(other.value)) - return false; - return true; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return String.format("SharedObjectEvent(%s, key: %s value: %s)", getType(), getKey(), getValue()); - } - - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - type = (Type) in.readObject(); - key = (String) in.readObject(); - value = in.readObject(); - } - - public void writeExternal(ObjectOutput out) throws IOException { - out.writeObject(type); - out.writeObject(key); - out.writeObject(value); - } -} diff --git a/src/main/java/org/red5/server/so/SharedObjectMessage.java b/src/main/java/org/red5/server/so/SharedObjectMessage.java deleted file mode 100644 index d528622ab..000000000 --- a/src/main/java/org/red5/server/so/SharedObjectMessage.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.so; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.red5.server.api.event.IEventListener; -import org.red5.server.net.rtmp.event.BaseEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Shared object event - */ -public class SharedObjectMessage extends BaseEvent implements ISharedObjectMessage { - - private static Logger log = LoggerFactory.getLogger(SharedObjectMessage.class); - - private static final long serialVersionUID = -8128704039659990049L; - - /** - * SO event name - */ - private String name; - - /** - * SO events chain - */ - private ConcurrentLinkedQueue events = new ConcurrentLinkedQueue(); - - /** - * SO version, used for synchronization purposes - */ - private int version; - - /** - * Whether SO persistent - */ - private boolean persistent; - - public SharedObjectMessage() { - } - - /** - * Creates Shared Object event with given name, version and persistence flag - * - * @param name Event name - * @param version SO version - * @param persistent SO persistence flag - */ - public SharedObjectMessage(String name, int version, boolean persistent) { - this(null, name, version, persistent); - } - - /** - * Creates Shared Object event with given listener, name, SO version and - * persistence flag - * - * @param source Event listener - * @param name Event name - * @param version SO version - * @param persistent SO persistence flag - */ - public SharedObjectMessage(IEventListener source, String name, int version, boolean persistent) { - super(Type.SHARED_OBJECT, source); - this.name = name; - this.version = version; - this.persistent = persistent; - } - - /** - * Resets the version and events to an initial state. - */ - public void reset() { - version = 0; - events.clear(); - } - - /** {@inheritDoc} */ - @Override - public byte getDataType() { - return TYPE_SHARED_OBJECT; - } - - /** {@inheritDoc} */ - public int getVersion() { - return version; - } - - /** - * Setter for version - * - * @param version - * New version - */ - protected void setVersion(int version) { - this.version = version; - } - - /** {@inheritDoc} */ - public String getName() { - return name; - } - - /** - * Setter for name - * - * @param name - * Event name - */ - protected void setName(String name) { - this.name = name; - } - - /** {@inheritDoc} */ - public boolean isPersistent() { - return persistent; - } - - /** - * Setter for persistence flag - * - * @param persistent - * Persistence flag - */ - protected void setPersistent(boolean persistent) { - this.persistent = persistent; - } - - /** {@inheritDoc} */ - public void addEvent(ISharedObjectEvent event) { - events.add(event); - } - - public void addEvents(List events) { - this.events.addAll(events); - } - - public void addEvents(Queue events) { - this.events.addAll(events); - } - - /** {@inheritDoc} */ - public ConcurrentLinkedQueue getEvents() { - return events; - } - - /** {@inheritDoc} */ - public boolean addEvent(ISharedObjectEvent.Type type, String key, Object value) { - SharedObjectEvent event = new SharedObjectEvent(type, key, value); - if (!events.contains(event)) { - return events.add(event); - } - return false; - } - - /** {@inheritDoc} */ - public void clear() { - events.clear(); - } - - /** {@inheritDoc} */ - public boolean isEmpty() { - return events.isEmpty(); - } - - /** {@inheritDoc} */ - @Override - public Type getType() { - return Type.SHARED_OBJECT; - } - - /** {@inheritDoc} */ - @Override - public Object getObject() { - return getEvents(); - } - - /** {@inheritDoc} */ - @Override - protected void releaseInternal() { - } - - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()); - sb.append(": ").append(name).append(" v=").append(version).append(" persistent=").append(persistent).append(" { "); - for (ISharedObjectEvent event : events) { - sb.append(event); - sb.append(' '); - } - sb.append('}'); - return sb.toString(); - } - - @SuppressWarnings({ "unchecked" }) - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - super.readExternal(in); - name = (String) in.readObject(); - version = in.readInt(); - persistent = in.readBoolean(); - Object o = in.readObject(); - if (o != null) { - log.trace("events type: {}", o.getClass().getName()); - if (o instanceof ConcurrentLinkedQueue) { - events = (ConcurrentLinkedQueue) o; - } - } - if (log.isTraceEnabled()) { - log.trace("readExternal: {}", toString()); - } - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - super.writeExternal(out); - if (log.isTraceEnabled()) { - log.trace("writeExternal: {}", toString()); - } - out.writeObject(name); - out.writeInt(version); - out.writeBoolean(persistent); - out.writeObject(events); - } - -} diff --git a/src/main/java/org/red5/server/so/SharedObjectScope.java b/src/main/java/org/red5/server/so/SharedObjectScope.java deleted file mode 100644 index 015e8ff7d..000000000 --- a/src/main/java/org/red5/server/so/SharedObjectScope.java +++ /dev/null @@ -1,863 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.so; - -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.locks.ReentrantLock; - -import org.red5.server.BaseConnection; -import org.red5.server.api.IAttributeStore; -import org.red5.server.api.IContext; -import org.red5.server.api.event.IEvent; -import org.red5.server.api.event.IEventListener; -import org.red5.server.api.persistence.IPersistenceStore; -import org.red5.server.api.scheduling.IScheduledJob; -import org.red5.server.api.scheduling.ISchedulingService; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.ScopeType; -import org.red5.server.api.so.ISharedObject; -import org.red5.server.api.so.ISharedObjectListener; -import org.red5.server.api.so.ISharedObjectSecurity; -import org.red5.server.api.so.ISharedObjectSecurityService; -import org.red5.server.api.statistics.ISharedObjectStatistics; -import org.red5.server.net.rtmp.status.StatusCodes; -import org.red5.server.scheduling.QuartzSchedulingService; -import org.red5.server.scope.BasicScope; -import org.red5.server.service.ReflectionUtils; -import org.red5.server.util.ScopeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Special scope for shared objects - */ -public class SharedObjectScope extends BasicScope implements ISharedObject, StatusCodes { - - private Logger log = LoggerFactory.getLogger(SharedObjectScope.class); - - /** - * Lock to synchronize shared object updates from multiple threads - */ - private final ReentrantLock lock = new ReentrantLock(); - - /** - * Server-side listeners - */ - private CopyOnWriteArraySet serverListeners = new CopyOnWriteArraySet(); - - /** - * Event handlers - */ - private ConcurrentMap handlers = new ConcurrentHashMap(1, 0.9f, 1); - - /** - * Security handlers - */ - private CopyOnWriteArraySet securityHandlers = new CopyOnWriteArraySet(); - - /** - * Scoped shared object - */ - protected volatile SharedObject so; - - /** - * Time to linger before checking for disposal - */ - private long lingerPeriod = 5000L; - - /** - * Linger job name - */ - private String lingerJobName; - - /** - * Creates shared object with given parent scope, name, persistence flag - * state and store object - * - * @param parent - * Parent scope - * @param name - * Name - * @param persistent - * Persistence flag state - * @param store - * Persistence store - */ - public SharedObjectScope(IScope parent, String name, boolean persistent, IPersistenceStore store) { - super(parent, ScopeType.SHARED_OBJECT, name, persistent); - // create shared object wrapper around the attributes - String path = parent.getContextPath(); - if ("".equals(path) || path.charAt(0) != '/') { - path = '/' + path; - } - log.trace("Path+name: {}/{}", path, name); - // Load SO - so = (SharedObject) store.load(ScopeType.SHARED_OBJECT + path + '/' - + name); - // Create if it doesn't exist - if (so == null) { - so = new SharedObject(name, path, persistent, store); - // Save - store.save(so); - } else { - // set path - so.setPath(path); - } - } - - /** {@inheritDoc} */ - public void registerSharedObjectSecurity(ISharedObjectSecurity handler) { - securityHandlers.add(handler); - } - - /** {@inheritDoc} */ - public void unregisterSharedObjectSecurity(ISharedObjectSecurity handler) { - securityHandlers.remove(handler); - } - - /** {@inheritDoc} */ - public Set getSharedObjectSecurity() { - return Collections.unmodifiableSet(securityHandlers); - } - - /** {@inheritDoc} */ - @Override - public IPersistenceStore getStore() { - return so.getStore(); - } - - /** {@inheritDoc} */ - @Override - public String getName() { - return so.getName(); - } - - /** {@inheritDoc} */ - @Override - public String getPath() { - return so.getPath(); - } - - public void setPath(String path) { - so.setPath(path); - } - - /** {@inheritDoc} */ - public boolean isPersistent() { - return so.isPersistent(); - } - - /** {@inheritDoc} */ - public void beginUpdate() { - // Make sure only one thread can update the SO - lock.lock(); - so.beginUpdate(); - } - - /** {@inheritDoc} */ - public void beginUpdate(IEventListener listener) { - // Make sure only one thread can update the SO - lock.lock(); - // start updates - so.beginUpdate(listener); - } - - /** {@inheritDoc} */ - public void endUpdate() { - // end update of SO - try { - so.endUpdate(); - } catch (Exception ex) { - log.warn("Exception on so.endUpdate", ex); - } finally { - lock.unlock(); - } - } - - /** {@inheritDoc} */ - public int getVersion() { - return so.getVersion(); - } - - /** {@inheritDoc} */ - public void sendMessage(String handler, List arguments) { - if (so != null) { - beginUpdate(); - try { - so.sendMessage(handler, arguments); - } catch (Exception ex) { - log.warn("Exception on so.sendMessage", ex); - } finally { - endUpdate(); - } - // Invoke method on registered handler - String serviceName, serviceMethod; - // Find out last dot position - int dotPos = handler.lastIndexOf('.'); - // If any, split service name and service method name - if (dotPos != -1) { - serviceName = handler.substring(0, dotPos); - serviceMethod = handler.substring(dotPos + 1); - } else { - // Otherwise only service method name is available - serviceName = ""; - serviceMethod = handler; - } - // Get previously registered handler for service - Object soHandler = getServiceHandler(serviceName); - if (soHandler == null && hasParent()) { - // No custom handler, check for service defined in the scope's context - IContext context = getParent().getContext(); - String serviceId = null; - try { - // The bean must have a name of - // "..soservice" - serviceId = so.getName() + '.' + serviceName + ".soservice"; - if (context.hasBean(serviceId)) { - soHandler = context.getBean(serviceId); - } - } catch (Exception err) { - log.debug("No such bean: {}", serviceId); - } - } - // Once handler is found, find matching method - if (soHandler != null) { - // With exact params... - Object[] methodResult = ReflectionUtils.findMethodWithExactParameters(soHandler, serviceMethod, arguments); - // Or at least with suitable list params - if (methodResult.length == 0 || methodResult[0] == null) { - methodResult = ReflectionUtils.findMethodWithListParameters(soHandler, serviceMethod, arguments); - } - // If method is found... - if (methodResult.length > 0 && methodResult[0] != null) { - Method method = (Method) methodResult[0]; - Object[] params = (Object[]) methodResult[1]; - // ...try to invoke it and handle exceptions - try { - method.invoke(soHandler, params); - } catch (Exception err) { - log.error("Error while invoking method {} on shared object handler {}", new Object[] { serviceMethod, handler }, err); - } - } - } - // notify server listeners - for (ISharedObjectListener listener : serverListeners) { - listener.onSharedObjectSend(this, handler, arguments); - } - } - } - - /** {@inheritDoc} */ - @Override - public boolean removeAttribute(String name) { - boolean success = false; - // begin update of shared object - beginUpdate(); - try { - // try to remove attribute - success = so.removeAttribute(name); - } catch (Exception ex) { - log.warn("Exception on so.removeAttribute", ex); - } finally { - // end update of SO - endUpdate(); - } - // notify listeners on success and return true - if (success) { - for (ISharedObjectListener listener : serverListeners) { - listener.onSharedObjectDelete(this, name); - } - } - return success; - } - - /** {@inheritDoc} */ - @Override - public void removeAttributes() { - beginUpdate(); - try { - // remove all attributes - so.removeAttributes(); - } catch (Exception ex) { - log.warn("Exception on so.removeAttributes", ex); - } finally { - endUpdate(); - } - // notify listeners on attributes clear - for (ISharedObjectListener listener : serverListeners) { - listener.onSharedObjectClear(this); - } - } - - /** {@inheritDoc} */ - public int size() { - return so != null ? so.getAttributeNames().size() : 0; - } - - /** {@inheritDoc} */ - @Override - public boolean addEventListener(IEventListener listener) { - boolean result = super.addEventListener(listener) && so.register(listener); - for (ISharedObjectListener soListener : serverListeners) { - soListener.onSharedObjectConnect(this); - } - return result; - } - - /** {@inheritDoc} */ - @Override - public boolean removeEventListener(IEventListener listener) { - // remove the listener from the so - so.unregister(listener); - // if we have not been released by all that acquired then keep on - // disconnection of the last listener - if (so.isAcquired()) { - log.debug("Shared object has been aquired so setting keep on disconnect"); - keepOnDisconnect = true; - } - // remove the listener - boolean result = super.removeEventListener(listener); - // notify other listeners that someone has stopped listening - for (ISharedObjectListener soListener : serverListeners) { - soListener.onSharedObjectDisconnect(this); - } - // check that linger job has be set - if (lingerJobName == null) { - // start a job to allow the so to linger for just a few ticks - QuartzSchedulingService scheduler = (QuartzSchedulingService) getParent().getContext().getBean(QuartzSchedulingService.BEAN_NAME); - IScheduledJob job = new IScheduledJob() { - public void execute(ISchedulingService service) { - if (so != null && !so.isClosed()) { - so.checkRelease(); - } - } - }; - lingerJobName = scheduler.addScheduledOnceJob(lingerPeriod, job); - } - // check acquire - if (so.isClosed()) { - log.debug("Removing scope: {}", this); - getParent().removeChildScope(this); - } - return result; - } - - /** {@inheritDoc} */ - @Override - public boolean hasAttribute(String name) { - return so.hasAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public Object getAttribute(String name) { - return so.getAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public Object getAttribute(String name, Object value) { - beginUpdate(); - try { - return so.getAttribute(name, value); - } catch (Exception ex) { - log.warn("Exception on so.getAttribute", ex); - } finally { - endUpdate(); - } - return null; - } - - /** {@inheritDoc} */ - @Override - public Map getAttributes() { - return so.getAttributes(); - } - - /** {@inheritDoc} */ - @Override - public Set getAttributeNames() { - return so.getAttributeNames(); - } - - /** {@inheritDoc} */ - @Override - public Boolean getBoolAttribute(String name) { - return so.getBoolAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public Byte getByteAttribute(String name) { - return so.getByteAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public Double getDoubleAttribute(String name) { - return so.getDoubleAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public Integer getIntAttribute(String name) { - return so.getIntAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public List getListAttribute(String name) { - return so.getListAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public Long getLongAttribute(String name) { - return so.getLongAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public Map getMapAttribute(String name) { - return so.getMapAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public Set getSetAttribute(String name) { - return so.getSetAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public Short getShortAttribute(String name) { - return so.getShortAttribute(name); - } - - /** {@inheritDoc} */ - @Override - public String getStringAttribute(String name) { - return so.getStringAttribute(name); - } - - /** {@inheritDoc} */ - public Map getData() { - return so.getData(); - } - - /** - * Return security handlers for this shared object or null if none are found. - * - * @return set of security handlers - */ - private Set getSecurityHandlers() { - ISharedObjectSecurityService security = (ISharedObjectSecurityService) ScopeUtils.getScopeService(getParent(), ISharedObjectSecurityService.class); - if (security == null) { - return null; - } - return security.getSharedObjectSecurity(); - } - - /** - * Call handlers and check if connection to the existing SO is allowed. - * - * @return is connection allowed - */ - protected boolean isConnectionAllowed() { - // Check internal handlers first - for (ISharedObjectSecurity handler : securityHandlers) { - if (!handler.isConnectionAllowed(this)) { - return false; - } - } - // Check global SO handlers next - final Set handlers = getSecurityHandlers(); - if (handlers == null) { - return true; - } - for (ISharedObjectSecurity handler : handlers) { - if (!handler.isConnectionAllowed(this)) { - return false; - } - } - return true; - } - - /** - * Call handlers and check if writing to the SO is allowed. - * - * @param key - * key - * @param value - * value - * @return is write allowed - */ - protected boolean isWriteAllowed(String key, Object value) { - // check internal handlers first - for (ISharedObjectSecurity handler : securityHandlers) { - if (!handler.isWriteAllowed(this, key, value)) { - return false; - } - } - // check global SO handlers next - final Set handlers = getSecurityHandlers(); - if (handlers == null) { - return true; - } - for (ISharedObjectSecurity handler : handlers) { - if (!handler.isWriteAllowed(this, key, value)) { - return false; - } - } - return true; - } - - /** - * Call handlers and check if deleting a property from the SO is allowed. - * - * @param key - * key - * @return is delete allowed - */ - protected boolean isDeleteAllowed(String key) { - // check internal handlers first - for (ISharedObjectSecurity handler : securityHandlers) { - if (!handler.isDeleteAllowed(this, key)) { - return false; - } - } - // check global SO handlers next - final Set handlers = getSecurityHandlers(); - if (handlers == null) { - return true; - } - for (ISharedObjectSecurity handler : handlers) { - if (!handler.isDeleteAllowed(this, key)) { - return false; - } - } - return true; - } - - /** - * Call handlers and check if sending a message to the clients connected to - * the SO is allowed. - * - * @param message - * message - * @param arguments - * arguments - * @return is send allowed - */ - protected boolean isSendAllowed(String message, List arguments) { - // check internal handlers first - for (ISharedObjectSecurity handler : securityHandlers) { - if (!handler.isSendAllowed(this, message, arguments)) { - return false; - } - } - // check global SO handlers next - final Set handlers = getSecurityHandlers(); - if (handlers == null) { - return true; - } - for (ISharedObjectSecurity handler : handlers) { - if (!handler.isSendAllowed(this, message, arguments)) { - return false; - } - } - return true; - } - - /** {@inheritDoc} */ - @Override - public void dispatchEvent(IEvent e) { - if (e instanceof ISharedObjectMessage - || e.getType() == IEvent.Type.SHARED_OBJECT) { - ISharedObjectMessage msg = (ISharedObjectMessage) e; - if (msg.hasSource()) { - beginUpdate(msg.getSource()); - } else { - beginUpdate(); - } - try { - for (ISharedObjectEvent event : msg.getEvents()) { - final String key = event.getKey(); - switch (event.getType()) { - case SERVER_CONNECT: - if (!isConnectionAllowed()) { - so.returnError(SO_NO_READ_ACCESS); - } else if (msg.hasSource()) { - IEventListener source = msg.getSource(); - if (source instanceof BaseConnection) { - ((BaseConnection) source) - .registerBasicScope(this); - } else { - addEventListener(source); - } - } - break; - case SERVER_DISCONNECT: - if (msg.hasSource()) { - IEventListener source = msg.getSource(); - if (source instanceof BaseConnection) { - ((BaseConnection) source) - .unregisterBasicScope(this); - } else { - removeEventListener(source); - } - } - break; - case SERVER_SET_ATTRIBUTE: - final Object value = event.getValue(); - if (!isWriteAllowed(key, value)) { - so.returnAttributeValue(key); - so.returnError(SO_NO_WRITE_ACCESS); - } else { - setAttribute(key, value); - } - break; - case SERVER_DELETE_ATTRIBUTE: - if (!isDeleteAllowed(key)) { - so.returnAttributeValue(key); - so.returnError(SO_NO_WRITE_ACCESS); - } else { - removeAttribute(key); - } - break; - case SERVER_SEND_MESSAGE: - final List arguments = (List) event.getValue(); - // Ignore request silently if not allowed - if (isSendAllowed(key, arguments)) { - sendMessage(key, arguments); - } else { - log.debug("Send is not allowed for {}", key); - } - break; - default: - log.warn("Unknown SO event: {}", event.getType()); - } - } - } catch (Exception ex) { - log.warn("Exception on dispatchEvent", ex); - } finally { - endUpdate(); - } - } else { - // don't know how to handle this event. - super.dispatchEvent(e); - } - } - - /** {@inheritDoc} */ - @Override - public boolean setAttribute(String name, Object value) { - boolean success = false; - beginUpdate(); - try { - success = so.setAttribute(name, value); - } catch (Exception ex) { - log.warn("Exception on so.setAttribute", ex); - } finally { - endUpdate(); - } - if (success) { - for (ISharedObjectListener listener : serverListeners) { - listener.onSharedObjectUpdate(this, name, value); - } - } - return success; - } - - /** {@inheritDoc} */ - @Override - public boolean setAttributes(IAttributeStore values) { - boolean success = false; - beginUpdate(); - try { - success = so.setAttributes(values); - } catch (Exception ex) { - log.warn("Exception on so.setAttributes", ex); - } finally { - endUpdate(); - } - if (success) { - for (ISharedObjectListener listener : serverListeners) { - listener.onSharedObjectUpdate(this, values); - } - } - return success; - } - - /** {@inheritDoc} */ - @Override - public boolean setAttributes(Map values) { - boolean success = false; - beginUpdate(); - try { - success = so.setAttributes(values); - } catch (Exception ex) { - log.warn("Exception on so.setAttributes", ex); - } finally { - endUpdate(); - } - if (success) { - for (ISharedObjectListener listener : serverListeners) { - listener.onSharedObjectUpdate(this, values); - } - } - return success; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "SharedObjectScope: " + getName(); - } - - /** {@inheritDoc} */ - public void addSharedObjectListener(ISharedObjectListener listener) { - serverListeners.add(listener); - } - - /** {@inheritDoc} */ - public void removeSharedObjectListener(ISharedObjectListener listener) { - serverListeners.remove(listener); - } - - /** {@inheritDoc} */ - public void registerServiceHandler(Object handler) { - registerServiceHandler("", handler); - } - - /** {@inheritDoc} */ - public void registerServiceHandler(String name, Object handler) { - if (name == null) { - name = ""; - } - handlers.put(name, handler); - } - - public void unregisterServiceHandler() { - unregisterServiceHandler(""); - } - - /** {@inheritDoc} */ - public void unregisterServiceHandler(String name) { - if (name == null) { - name = ""; - } - handlers.remove(name); - } - - /** {@inheritDoc} */ - public Object getServiceHandler(String name) { - if (name == null) { - name = ""; - } - return handlers.get(name); - } - - /** {@inheritDoc} */ - public Set getServiceHandlerNames() { - return Collections.unmodifiableSet(handlers.keySet()); - } - - /** - * Locks the shared object instance. Prevents any changes to this object by - * clients until the SharedObject.unlock() method is called. - */ - public void lock() { - lock.lock(); - } - - /** - * Unlocks a shared object instance that was locked with - * SharedObject.lock(). - */ - public void unlock() { - lock.unlock(); - } - - /** - * Returns the locked state of this SharedObject. - * - * @return true if in a locked state; false otherwise - */ - public boolean isLocked() { - return lock.isLocked(); - } - - /** {@inheritDoc} */ - public boolean clear() { - boolean success = false; - beginUpdate(); - try { - success = so.clear(); - } catch (Exception ex) { - log.warn("Exception on so.clear", ex); - } finally { - endUpdate(); - } - if (success) { - for (ISharedObjectListener listener : serverListeners) { - listener.onSharedObjectClear(this); - } - } - return success; - } - - /** {@inheritDoc} */ - public void close() { - // close the internal SO - so.close(); - // remove from the parent - parent.removeChildScope(this); - // clear the reference - so = null; - } - - /** {@inheritDoc} */ - public void acquire() { - so.acquire(); - } - - /** {@inheritDoc} */ - public boolean isAcquired() { - return so.isAcquired(); - } - - /** {@inheritDoc} */ - public void release() { - so.release(); - } - - /** {@inheritDoc} */ - public ISharedObjectStatistics getStatistics() { - return so; - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/so/SharedObjectService.java b/src/main/java/org/red5/server/so/SharedObjectService.java deleted file mode 100644 index e42fbf9a9..000000000 --- a/src/main/java/org/red5/server/so/SharedObjectService.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.so; - -import java.util.Set; - -import org.red5.server.api.persistence.IPersistable; -import org.red5.server.api.persistence.IPersistenceStore; -import org.red5.server.api.persistence.PersistenceUtils; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.ScopeType; -import org.red5.server.api.so.ISharedObject; -import org.red5.server.api.so.ISharedObjectService; -import org.red5.server.persistence.RamPersistence; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; - -/** - * Shared object service - */ -public class SharedObjectService implements ISharedObjectService { - - private Logger log = LoggerFactory.getLogger(SharedObjectService.class); - - /** - * Persistence store prefix - */ - private static final String SO_PERSISTENCE_STORE = IPersistable.TRANSIENT_PREFIX + "_SO_PERSISTENCE_STORE_"; - - /** - * Transient store prefix - */ - private static final String SO_TRANSIENT_STORE = IPersistable.TRANSIENT_PREFIX + "_SO_TRANSIENT_STORE_"; - - /** - * Service used to provide updates / notifications. - */ - private static ThreadPoolTaskScheduler scheduler; - - /** - * Maximum messages to send at once - */ - public static int MAXIMUM_EVENTS_PER_UPDATE = 16; - - /** - * Persistence class name - */ - private String persistenceClassName = "org.red5.server.persistence.RamPersistence"; - - /** - * Pushes a task to the scheduler for single execution. - * - * @param task - */ - public static void submitTask(Runnable task) { - scheduler.execute(task); - } - - /** - * @param maximumEventsPerUpdate the maximumEventsPerUpdate to set - */ - public void setMaximumEventsPerUpdate(int maximumEventsPerUpdate) { - MAXIMUM_EVENTS_PER_UPDATE = maximumEventsPerUpdate; - } - - /** - * Setter for persistence class name. - * - * @param name Setter for persistence class name - */ - public void setPersistenceClassName(String name) { - persistenceClassName = name; - } - - /** - * @param scheduler the scheduler to set - */ - public static void setScheduler(ThreadPoolTaskScheduler scheduler) { - SharedObjectService.scheduler = scheduler; - } - - /** - * Return scope store - * - * @param scope Scope - * @param persistent Persistent store or not? - * @return Scope's store - */ - private IPersistenceStore getStore(IScope scope, boolean persistent) { - IPersistenceStore store; - if (!persistent) { - // Use special store for non-persistent shared objects - if (!scope.hasAttribute(SO_TRANSIENT_STORE)) { - store = new RamPersistence(scope); - scope.setAttribute(SO_TRANSIENT_STORE, store); - return store; - } - return (IPersistenceStore) scope.getAttribute(SO_TRANSIENT_STORE); - } - // Evaluate configuration for persistent shared objects - if (!scope.hasAttribute(SO_PERSISTENCE_STORE)) { - try { - store = PersistenceUtils.getPersistenceStore(scope, persistenceClassName); - log.info("Created persistence store {} for shared objects", store); - } catch (Exception err) { - log.warn("Could not create persistence store ({}) for shared objects, falling back to Ram persistence", persistenceClassName, err); - store = new RamPersistence(scope); - } - scope.setAttribute(SO_PERSISTENCE_STORE, store); - return store; - } - return (IPersistenceStore) scope.getAttribute(SO_PERSISTENCE_STORE); - } - - /** {@inheritDoc} */ - public boolean createSharedObject(IScope scope, String name, boolean persistent) { - boolean added = hasSharedObject(scope, name); - if (!added) { - log.debug("Attempting to add shared object: {} to {}", name, scope.getName()); - added = scope.addChildScope(new SharedObjectScope(scope, name, persistent, getStore(scope, persistent))); - if (!added) { - added = hasSharedObject(scope, name); - log.debug("Add failed on create, shared object already exists: {}", added); - } - } else { - // the shared object already exists - log.trace("Shared object ({}) already exists. Persistent: {}", name, persistent); - } - // added or already existing will be true - return added; - } - - /** {@inheritDoc} */ - public ISharedObject getSharedObject(IScope scope, String name) { - return (ISharedObject) scope.getBasicScope(ScopeType.SHARED_OBJECT, name); - } - - /** {@inheritDoc} */ - public ISharedObject getSharedObject(IScope scope, String name, boolean persistent) { - if (!hasSharedObject(scope, name)) { - createSharedObject(scope, name, persistent); - } - return getSharedObject(scope, name); - } - - /** {@inheritDoc} */ - public Set getSharedObjectNames(IScope scope) { - return scope.getBasicScopeNames(ScopeType.SHARED_OBJECT); - } - - /** {@inheritDoc} */ - public boolean hasSharedObject(IScope scope, String name) { - return scope.hasChildScope(ScopeType.SHARED_OBJECT, name); - } - - /** {@inheritDoc} */ - public boolean clearSharedObjects(IScope scope, String name) { - boolean result = false; - if (hasSharedObject(scope, name)) { - // '/' clears all local and persistent shared objects associated with the instance - // /foo/bar clears the shared object /foo/bar; if bar is a directory name, no shared objects are deleted. - // /foo/bar/* clears all shared objects stored under the instance directory /foo/bar. - // The bar directory is also deleted if no persistent shared objects are in use within this namespace. - // /foo/bar/XX?? clears all shared objects that begin with XX, followed by any two characters. If a directory name matches - // this specification, all the shared objects within this directory are cleared. - result = ((ISharedObject) scope.getBasicScope(ScopeType.SHARED_OBJECT, name)).clear(); - } - return result; - } - -} diff --git a/src/main/java/org/red5/server/stream/AbstractClientStream.java b/src/main/java/org/red5/server/stream/AbstractClientStream.java deleted file mode 100644 index acf64fd30..000000000 --- a/src/main/java/org/red5/server/stream/AbstractClientStream.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.lang.ref.WeakReference; - -import org.red5.server.api.stream.IClientStream; -import org.red5.server.api.stream.IStreamCapableConnection; - -/** - * Abstract base for client streams - */ -public abstract class AbstractClientStream extends AbstractStream implements IClientStream { - - /** - * Stream identifier. Unique across server. - */ - private int streamId; - - /** - * Stream name of the broadcasting stream. - */ - private String broadcastStreamPublishName; - - /** - * Connection that works with streams - */ - private WeakReference conn; - - /** - * Buffer duration in ms as requested by the client - */ - private int clientBufferDuration; - - /** - * Setter for stream id - * @param streamId Stream id - */ - public void setStreamId(int streamId) { - this.streamId = streamId; - } - - /** - * Return stream id - * @return Stream id - */ - public int getStreamId() { - return streamId; - } - - /** - * Setter for stream capable connection - * @param conn IStreamCapableConnection object - */ - public void setConnection(IStreamCapableConnection conn) { - this.conn = new WeakReference(conn); - } - - /** - * Return connection associated with stream - * @return Stream capable connection object - */ - public IStreamCapableConnection getConnection() { - return conn.get(); - } - - /** {@inheritDoc} */ - public void setClientBufferDuration(int duration) { - clientBufferDuration = duration; - } - - /** - * Get duration in ms as requested by the client. - * - * @return value - */ - public int getClientBufferDuration() { - return clientBufferDuration; - } - - /** - * Sets the broadcasting streams name. - * - * @param broadcastStreamPublishName name of the broadcasting stream - */ - public void setBroadcastStreamPublishName(String broadcastStreamPublishName) { - this.broadcastStreamPublishName = broadcastStreamPublishName; - } - - /** {@inheritDoc} */ - public String getBroadcastStreamPublishName() { - return broadcastStreamPublishName; - } - -} diff --git a/src/main/java/org/red5/server/stream/AbstractStream.java b/src/main/java/org/red5/server/stream/AbstractStream.java deleted file mode 100644 index ec1713f9a..000000000 --- a/src/main/java/org/red5/server/stream/AbstractStream.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.util.concurrent.Semaphore; - -import org.red5.codec.IStreamCodecInfo; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeHandler; -import org.red5.server.api.stream.IStream; -import org.red5.server.api.stream.IStreamAwareScopeHandler; -import org.red5.server.api.stream.StreamState; -import org.red5.server.net.rtmp.event.Notify; - -/** - * Abstract base implementation of IStream. Contains codec information, stream name, scope, event handling - * meand, provides stream start and stop operations. - * - * @see org.red5.server.api.stream.IStream - */ -public abstract class AbstractStream implements IStream { - - /** - * Current state - */ - protected StreamState state = StreamState.UNINIT; - - /** - * Stream name - */ - private String name; - - /** - * Stream audio and video codec information - */ - private IStreamCodecInfo codecInfo; - - /** - * Stores the streams metadata - */ - protected Notify metaData; - - /** - * Stream scope - */ - private IScope scope; - - /** - * Timestamp the stream was created. - */ - protected long creationTime; - - /** - * Lock for protecting critical sections - */ - protected final Semaphore lock = new Semaphore(1, true); - - /** - * Return stream name - * @return Stream name - */ - public String getName() { - return name; - } - - /** - * Return codec information - * @return Stream codec information - */ - public IStreamCodecInfo getCodecInfo() { - return codecInfo; - } - - /** - * Returns the metadata for the associated stream, if it exists. - * - * @return stream meta data - */ - public Notify getMetaData() { - return metaData; - } - - /** - * Return scope - * @return Scope - */ - public IScope getScope() { - return scope; - } - - /** - * Returns timestamp at which the stream was created. - * - * @return creation timestamp - */ - public long getCreationTime() { - return creationTime; - } - - /** - * Setter for name - * @param name Stream name - */ - public void setName(String name) { - this.name = name; - } - - /** - * Setter for codec info - * @param codecInfo Codec info - */ - public void setCodecInfo(IStreamCodecInfo codecInfo) { - this.codecInfo = codecInfo; - } - - /** - * Setter for scope - * @param scope Scope - */ - public void setScope(IScope scope) { - this.scope = scope; - } - - /** - * Return stream state - * @return StreamState - */ - public StreamState getState() { - try { - lock.acquireUninterruptibly(); - return state; - } finally { - lock.release(); - } - } - - /** - * Sets the stream state - * @param state - */ - public void setState(StreamState state) { - if (!this.state.equals(state)) { - try { - lock.acquireUninterruptibly(); - this.state = state; - } finally { - lock.release(); - } - } - } - - /** - * Return stream aware scope handler or null if scope is null - * @return IStreamAwareScopeHandler implementation - */ - protected IStreamAwareScopeHandler getStreamAwareHandler() { - if (scope != null) { - IScopeHandler handler = scope.getHandler(); - if (handler instanceof IStreamAwareScopeHandler) { - return (IStreamAwareScopeHandler) handler; - } - } - return null; - } -} diff --git a/src/main/java/org/red5/server/stream/AudioCodecFactory.java b/src/main/java/org/red5/server/stream/AudioCodecFactory.java deleted file mode 100644 index 7dbf60634..000000000 --- a/src/main/java/org/red5/server/stream/AudioCodecFactory.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.codec.IAudioStreamCodec; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Factory for audio codecs. Creates and returns audio codecs - * - * @author The Red5 Project - * @author Vladimir Hmelyoff (vlhm@splitmedialabs.com) - */ -public class AudioCodecFactory { - /** - * Object key - */ - public static final String KEY = "audioCodecFactory"; - - /** - * Logger for audio factory - */ - private static Logger log = LoggerFactory.getLogger(AudioCodecFactory.class); - - /** - * List of available codecs - */ - private static List codecs = new ArrayList(1); - - /** - * Setter for codecs - * - * @param codecs List of codecs - */ - public void setCodecs(List codecs) { - AudioCodecFactory.codecs = codecs; - } - - /** - * Create and return new audio codec applicable for byte buffer data - * @param data Byte buffer data - * @return audio codec - */ - public static IAudioStreamCodec getAudioCodec(IoBuffer data) { - IAudioStreamCodec result = null; - try { - //get the codec identifying byte - int codecId = (data.get() & 0xf0) >> 4; - switch (codecId) { - case 10: //aac - result = (IAudioStreamCodec) Class.forName("org.red5.codec.AACAudio").newInstance(); - break; - // TODO add SPEEX support? - } - data.rewind(); - } catch (Exception ex) { - log.error("Error creating codec instance", ex); - } - //if codec is not found do the old-style loop - if (result == null) { - for (IAudioStreamCodec storedCodec: codecs) { - IAudioStreamCodec codec; - // XXX: this is a bit of a hack to create new instances of the - // configured audio codec for each stream - try { - codec = storedCodec.getClass().newInstance(); - } catch (Exception e) { - log.error("Could not create audio codec instance", e); - continue; - } - if (codec.canHandleData(data)) { - result = codec; - break; - } - } - } - return result; - } - -} diff --git a/src/main/java/org/red5/server/stream/ClientBroadcastStream.java b/src/main/java/org/red5/server/stream/ClientBroadcastStream.java deleted file mode 100644 index 1a239be11..000000000 --- a/src/main/java/org/red5/server/stream/ClientBroadcastStream.java +++ /dev/null @@ -1,929 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.io.File; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.ref.WeakReference; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - -import javax.management.InstanceAlreadyExistsException; -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.management.StandardMBean; - -import org.apache.commons.lang3.StringUtils; -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.codec.IAudioStreamCodec; -import org.red5.codec.IStreamCodecInfo; -import org.red5.codec.IVideoStreamCodec; -import org.red5.codec.StreamCodecInfo; -import org.red5.server.api.IConnection; -import org.red5.server.api.Red5; -import org.red5.server.api.event.IEvent; -import org.red5.server.api.event.IEventDispatcher; -import org.red5.server.api.event.IEventListener; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.statistics.IClientBroadcastStreamStatistics; -import org.red5.server.api.statistics.support.StatisticsCounter; -import org.red5.server.api.stream.IClientBroadcastStream; -import org.red5.server.api.stream.IStreamAwareScopeHandler; -import org.red5.server.api.stream.IStreamCapableConnection; -import org.red5.server.api.stream.IStreamListener; -import org.red5.server.api.stream.IStreamPacket; -import org.red5.server.jmx.mxbeans.ClientBroadcastStreamMXBean; -import org.red5.server.messaging.IConsumer; -import org.red5.server.messaging.IFilter; -import org.red5.server.messaging.IMessage; -import org.red5.server.messaging.IMessageComponent; -import org.red5.server.messaging.IMessageOutput; -import org.red5.server.messaging.IPipe; -import org.red5.server.messaging.IPipeConnectionListener; -import org.red5.server.messaging.IProvider; -import org.red5.server.messaging.IPushableConsumer; -import org.red5.server.messaging.OOBControlMessage; -import org.red5.server.messaging.PipeConnectionEvent; -import org.red5.server.net.rtmp.event.AudioData; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.Invoke; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.net.rtmp.status.Status; -import org.red5.server.net.rtmp.status.StatusCodes; -import org.red5.server.stream.message.RTMPMessage; -import org.red5.server.stream.message.StatusMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.jmx.export.annotation.ManagedResource; - -/** - * Represents live stream broadcasted from client. As Flash Media Server, Red5 supports - * recording mode for live streams, that is, broadcasted stream has broadcast mode. It can be either - * "live" or "record" and latter causes server-side application to record broadcasted stream. - * - * Note that recorded streams are recorded as FLV files. - * - * This type of stream uses two different pipes for live streaming and recording. - * - * @author The Red5 Project - * @author Steven Gong - * @author Paul Gregoire (mondain@gmail.com) - * @author Vladimir Hmelyoff (vlhm@splitmedialabs.com) - */ -@ManagedResource(objectName = "org.red5.server:type=ClientBroadcastStream", description = "ClientBroadcastStream") -public class ClientBroadcastStream extends AbstractClientStream implements IClientBroadcastStream, IFilter, IPushableConsumer, IPipeConnectionListener, IEventDispatcher, - IClientBroadcastStreamStatistics, ClientBroadcastStreamMXBean { - - private static final Logger log = LoggerFactory.getLogger(ClientBroadcastStream.class); - - /** - * Whether or not to automatically record the associated stream. - */ - protected boolean automaticRecording; - - /** - * Total number of bytes received. - */ - protected long bytesReceived; - - /** - * Is there need to check video codec? - */ - protected boolean checkVideoCodec = false; - - /** - * Is there need to check audio codec? - */ - protected boolean checkAudioCodec = false; - - /** - * Data is sent by chunks, each of them has size - */ - protected int chunkSize; - - /** - * Is this stream still active? - */ - protected volatile boolean closed; - - /** - * Output endpoint that providers use - */ - protected IMessageOutput connMsgOut; - - /** - * Stores timestamp of first packet - */ - protected long firstPacketTime = -1; - - /** - * Pipe for live streaming - */ - protected IPipe livePipe; - - /** - * Stream published name - */ - protected String publishedName; - - /** - * Streaming parameters - */ - protected Map parameters; - - /** - * Is there need to send start notification? - */ - protected boolean sendStartNotification = true; - - /** - * Stores statistics about subscribers. - */ - private StatisticsCounter subscriberStats = new StatisticsCounter(); - - /** - * Listeners to get notified about received packets. - */ - protected Set listeners = new CopyOnWriteArraySet(); - - /** - * Recording listener - */ - private WeakReference recordingListener; - - protected long latestTimeStamp = -1; - - /** - * Whether or not to register with JMX. - */ - private boolean registerJMX = true; - - /** - * Check and send notification if necessary - * @param event Event - */ - private void checkSendNotifications(IEvent event) { - IEventListener source = event.getSource(); - sendStartNotifications(source); - } - - /** - * Closes stream, unsubscribes provides, sends stoppage notifications and broadcast close notification. - */ - public void close() { - log.debug("Stream close: {}", publishedName); - if (closed) { - log.debug("{} already closed", publishedName); - return; - } - closed = true; - if (livePipe != null) { - livePipe.unsubscribe((IProvider) this); - } - // if we have a recording listener, inform that this stream is done - if (recordingListener != null) { - sendRecordStopNotify(); - notifyRecordingStop(); - // inform the listener to finish and close - recordingListener.get().stop(); - } - sendPublishStopNotify(); - // TODO: can we send the client something to make sure he stops sending data? - if (connMsgOut != null) { - connMsgOut.unsubscribe(this); - } - notifyBroadcastClose(); - // clear the listener after all the notifications have been sent - if (recordingListener != null) { - recordingListener.clear(); - } - // clear listeners - if (!listeners.isEmpty()) { - listeners.clear(); - } - // deregister with jmx - unregisterJMX(); - } - - /** - * Dispatches event - * @param event Event to dispatch - */ - public void dispatchEvent(IEvent event) { - if (event instanceof IRTMPEvent && !closed) { - switch (event.getType()) { - case STREAM_CONTROL: - case STREAM_DATA: - // create the event - IRTMPEvent rtmpEvent; - try { - rtmpEvent = (IRTMPEvent) event; - } catch (ClassCastException e) { - log.error("Class cast exception in event dispatch", e); - return; - } - int eventTime = -1; - if (log.isTraceEnabled()) { - // If this is first packet save its timestamp; expect it is - // absolute? no matter: it's never used! - if (firstPacketTime == -1) { - firstPacketTime = rtmpEvent.getTimestamp(); - log.trace(String.format("CBS=@%08x: rtmpEvent=%s creation=%s firstPacketTime=%d", System.identityHashCode(this), rtmpEvent.getClass().getSimpleName(), - creationTime, firstPacketTime)); - } else { - log.trace(String.format("CBS=@%08x: rtmpEvent=%s creation=%s firstPacketTime=%d timestamp=%d", System.identityHashCode(this), rtmpEvent.getClass() - .getSimpleName(), creationTime, firstPacketTime, rtmpEvent.getTimestamp())); - } - - } - //get the buffer only once per call - IoBuffer buf = null; - if (rtmpEvent instanceof IStreamData && (buf = ((IStreamData) rtmpEvent).getData()) != null) { - bytesReceived += buf.limit(); - } - // get stream codec - IStreamCodecInfo codecInfo = getCodecInfo(); - StreamCodecInfo info = null; - if (codecInfo instanceof StreamCodecInfo) { - info = (StreamCodecInfo) codecInfo; - } - //log.trace("Stream codec info: {}", info); - if (rtmpEvent instanceof AudioData) { - IAudioStreamCodec audioStreamCodec = null; - if (checkAudioCodec) { - // dont try to read codec info from 0 length audio packets - if (buf.limit() > 0) { - audioStreamCodec = AudioCodecFactory.getAudioCodec(buf); - if (info != null) { - info.setAudioCodec(audioStreamCodec); - } - checkAudioCodec = false; - } - } else if (codecInfo != null) { - audioStreamCodec = codecInfo.getAudioCodec(); - } - if (audioStreamCodec != null) { - audioStreamCodec.addData(buf.asReadOnlyBuffer()); - } - if (info != null) { - info.setHasAudio(true); - } - eventTime = rtmpEvent.getTimestamp(); - log.trace("Audio: {}", eventTime); - } else if (rtmpEvent instanceof VideoData) { - IVideoStreamCodec videoStreamCodec = null; - if (checkVideoCodec) { - videoStreamCodec = VideoCodecFactory.getVideoCodec(buf); - if (info != null) { - info.setVideoCodec(videoStreamCodec); - } - checkVideoCodec = false; - } else if (codecInfo != null) { - videoStreamCodec = codecInfo.getVideoCodec(); - } - if (videoStreamCodec != null) { - videoStreamCodec.addData(buf.asReadOnlyBuffer()); - } - if (info != null) { - info.setHasVideo(true); - } - eventTime = rtmpEvent.getTimestamp(); - log.trace("Video: {}", eventTime); - } else if (rtmpEvent instanceof Invoke) { - eventTime = rtmpEvent.getTimestamp(); - //do we want to return from here? - //event / stream listeners will not be notified of invokes - return; - } else if (rtmpEvent instanceof Notify) { - // store the metadata - Notify notifyEvent = (Notify) rtmpEvent; - if (metaData == null && notifyEvent.getHeader().getDataType() == Notify.TYPE_STREAM_METADATA) { - try { - metaData = notifyEvent.duplicate(); - } catch (Exception e) { - log.warn("Metadata could not be duplicated for this stream", e); - } - } - eventTime = rtmpEvent.getTimestamp(); - } - // update last event time - if (eventTime > latestTimeStamp) { - latestTimeStamp = eventTime; - } - // notify event listeners - checkSendNotifications(event); - // note this timestamp is set in event/body but not in the associated header - try { - // route to live - if (livePipe != null) { - // create new RTMP message, initialize it and push through pipe - RTMPMessage msg = RTMPMessage.build(rtmpEvent, eventTime); - livePipe.pushMessage(msg); - } else { - log.debug("Live pipe was null, message was not pushed"); - } - } catch (IOException err) { - stop(); - } - // notify listeners about received packet - if (rtmpEvent instanceof IStreamPacket) { - for (IStreamListener listener : getStreamListeners()) { - try { - listener.packetReceived(this, (IStreamPacket) rtmpEvent); - } catch (Exception e) { - log.error("Error while notifying listener {}", listener, e); - if (listener instanceof RecordingListener) { - sendRecordFailedNotify(e.getMessage()); - } - } - } - } - break; - default: - // ignored event - log.debug("Ignoring event: {}", event.getType()); - } - } else { - log.debug("Event was of wrong type or stream is closed ({})", closed); - } - } - - /** {@inheritDoc} */ - public int getActiveSubscribers() { - return subscriberStats.getCurrent(); - } - - /** {@inheritDoc} */ - public long getBytesReceived() { - return bytesReceived; - } - - /** {@inheritDoc} */ - public int getCurrentTimestamp() { - return (int) latestTimeStamp; - } - - /** {@inheritDoc} */ - public int getMaxSubscribers() { - return subscriberStats.getMax(); - } - - /** - * Getter for provider - * @return Provider - */ - public IProvider getProvider() { - return this; - } - - /** - * Setter for stream published name - * @param name Name that used for publishing. Set at client side when begin to broadcast with NetStream#publish. - */ - public void setPublishedName(String name) { - log.debug("setPublishedName: {}", name); - // a publish name of "false" is a special case, used when stopping a stream - if (StringUtils.isNotEmpty(name) && !"false".equals(name)) { - this.publishedName = name; - registerJMX(); - } - } - - /** - * Getter for published name - * @return Stream published name - */ - public String getPublishedName() { - return publishedName; - } - - /** {@inheritDoc} */ - public void setParameters(Map params) { - this.parameters = params; - } - - /** {@inheritDoc} */ - public Map getParameters() { - return parameters; - } - - /** {@inheritDoc} */ - public String getSaveFilename() { - if (recordingListener != null) { - return recordingListener.get().getFileName(); - } - return null; - } - - /** {@inheritDoc} */ - public IClientBroadcastStreamStatistics getStatistics() { - return this; - } - - /** {@inheritDoc} */ - public int getTotalSubscribers() { - return subscriberStats.getTotal(); - } - - /** - * @return the automaticRecording - */ - public boolean isAutomaticRecording() { - return automaticRecording; - } - - /** - * @param automaticRecording the automaticRecording to set - */ - public void setAutomaticRecording(boolean automaticRecording) { - this.automaticRecording = automaticRecording; - } - - /** - * @param registerJMX the registerJMX to set - */ - public void setRegisterJMX(boolean registerJMX) { - this.registerJMX = registerJMX; - } - - /** - * Notifies handler on stream broadcast close - */ - private void notifyBroadcastClose() { - final IStreamAwareScopeHandler handler = getStreamAwareHandler(); - if (handler != null) { - try { - handler.streamBroadcastClose(this); - } catch (Throwable t) { - log.error("Error in notifyBroadcastClose", t); - } - } - } - - /** - * Notifies handler on stream recording stop - */ - private void notifyRecordingStop() { - IStreamAwareScopeHandler handler = getStreamAwareHandler(); - if (handler != null) { - try { - handler.streamRecordStop(this); - } catch (Throwable t) { - log.error("Error in notifyBroadcastClose", t); - } - } - } - - /** - * Notifies handler on stream broadcast start - */ - private void notifyBroadcastStart() { - IStreamAwareScopeHandler handler = getStreamAwareHandler(); - if (handler != null) { - try { - handler.streamBroadcastStart(this); - } catch (Throwable t) { - log.error("Error in notifyBroadcastStart", t); - } - } - } - - /** - * Send OOB control message with chunk size - */ - private void notifyChunkSize() { - if (chunkSize > 0 && livePipe != null) { - OOBControlMessage setChunkSize = new OOBControlMessage(); - setChunkSize.setTarget("ConnectionConsumer"); - setChunkSize.setServiceName("chunkSize"); - if (setChunkSize.getServiceParamMap() == null) { - setChunkSize.setServiceParamMap(new HashMap()); - } - setChunkSize.getServiceParamMap().put("chunkSize", chunkSize); - livePipe.sendOOBControlMessage(getProvider(), setChunkSize); - } - } - - /** - * Out-of-band control message handler - * - * @param source OOB message source - * @param pipe Pipe that used to send OOB message - * @param oobCtrlMsg Out-of-band control message - */ - public void onOOBControlMessage(IMessageComponent source, IPipe pipe, OOBControlMessage oobCtrlMsg) { - String target = oobCtrlMsg.getTarget(); - if ("ClientBroadcastStream".equals(target)) { - String serviceName = oobCtrlMsg.getServiceName(); - if ("chunkSize".equals(serviceName)) { - chunkSize = (Integer) oobCtrlMsg.getServiceParamMap().get("chunkSize"); - notifyChunkSize(); - } else { - log.debug("Unhandled OOB control message for service: {}", serviceName); - } - } else { - log.debug("Unhandled OOB control message to target: {}", target); - } - } - - /** - * Pipe connection event handler - * @param event Pipe connection event - */ - @SuppressWarnings("unused") - public void onPipeConnectionEvent(PipeConnectionEvent event) { - switch (event.getType()) { - case PipeConnectionEvent.PROVIDER_CONNECT_PUSH: - log.debug("Provider connect"); - if (event.getProvider() == this && event.getSource() != connMsgOut && (event.getParamMap() == null || !event.getParamMap().containsKey("record"))) { - this.livePipe = (IPipe) event.getSource(); - log.debug("Provider: {}", this.livePipe.getClass().getName()); - for (IConsumer consumer : this.livePipe.getConsumers()) { - subscriberStats.increment(); - } - } - break; - case PipeConnectionEvent.PROVIDER_DISCONNECT: - log.debug("Provider disconnect"); - if (log.isDebugEnabled() && this.livePipe != null) { - log.debug("Provider: {}", this.livePipe.getClass().getName()); - } - if (this.livePipe == event.getSource()) { - this.livePipe = null; - } - break; - case PipeConnectionEvent.CONSUMER_CONNECT_PUSH: - log.debug("Consumer connect"); - IPipe pipe = (IPipe) event.getSource(); - if (log.isDebugEnabled() && pipe != null) { - log.debug("Consumer: {}", pipe.getClass().getName()); - } - if (this.livePipe == pipe) { - notifyChunkSize(); - } - subscriberStats.increment(); - break; - case PipeConnectionEvent.CONSUMER_DISCONNECT: - log.debug("Consumer disconnect"); - log.debug("Consumer: {}", event.getSource().getClass().getName()); - subscriberStats.decrement(); - break; - default: - } - } - - /** - * Currently not implemented - * - * @param pipe Pipe - * @param message Message - */ - public void pushMessage(IPipe pipe, IMessage message) { - } - - /** - * Save broadcasted stream. - * - * @param name Stream name - * @param isAppend Append mode - * @throws IOException File could not be created/written to - */ - public void saveAs(String name, boolean isAppend) throws IOException { - log.debug("SaveAs - name: {} append: {}", name, isAppend); - // get connection to check if client is still streaming - IStreamCapableConnection conn = getConnection(); - if (conn == null) { - throw new IOException("Stream is no longer connected"); - } - // one recording listener at a time via this entry point - if (recordingListener == null) { - // XXX Paul: Revisit this section to allow for implementation of custom IRecordingListener - //IRecordingListener listener = (IRecordingListener) ScopeUtils.getScopeService(conn.getScope(), IRecordingListener.class, RecordingListener.class, false); - // create a recording listener - IRecordingListener listener = new RecordingListener(); - log.debug("Created: {}", listener); - // initialize the listener - if (listener.init(conn, name, isAppend)) { - // get decoder info if it exists for the stream - IStreamCodecInfo codecInfo = getCodecInfo(); - log.debug("Codec info: {}", codecInfo); - if (codecInfo instanceof StreamCodecInfo) { - StreamCodecInfo info = (StreamCodecInfo) codecInfo; - IVideoStreamCodec videoCodec = info.getVideoCodec(); - log.debug("Video codec: {}", videoCodec); - if (videoCodec != null) { - //check for decoder configuration to send - IoBuffer config = videoCodec.getDecoderConfiguration(); - if (config != null) { - log.debug("Decoder configuration is available for {}", videoCodec.getName()); - VideoData videoConf = new VideoData(config.asReadOnlyBuffer()); - try { - log.debug("Setting decoder configuration for recording"); - listener.getFileConsumer().setVideoDecoderConfiguration(videoConf); - } finally { - videoConf.release(); - } - } - } else { - log.debug("Could not initialize stream output, videoCodec is null."); - } - IAudioStreamCodec audioCodec = info.getAudioCodec(); - log.debug("Audio codec: {}", audioCodec); - if (audioCodec != null) { - //check for decoder configuration to send - IoBuffer config = audioCodec.getDecoderConfiguration(); - if (config != null) { - log.debug("Decoder configuration is available for {}", audioCodec.getName()); - AudioData audioConf = new AudioData(config.asReadOnlyBuffer()); - try { - log.debug("Setting decoder configuration for recording"); - listener.getFileConsumer().setAudioDecoderConfiguration(audioConf); - } finally { - audioConf.release(); - } - } - } else { - log.debug("No decoder configuration available, audioCodec is null."); - } - } - // set as primary listener - recordingListener = new WeakReference(listener); - // add as a listener - addStreamListener(listener); - // start the listener thread - listener.start(); - } else { - log.warn("Recording listener failed to initialize for stream: {}", name); - } - } else { - log.debug("Recording listener already exists for stream: {} auto record enabled: {}", name, automaticRecording); - } - } - - /** - * Sends publish start notifications - */ - private void sendPublishStartNotify() { - Status publishStatus = new Status(StatusCodes.NS_PUBLISH_START); - publishStatus.setClientid(getStreamId()); - publishStatus.setDetails(getPublishedName()); - - StatusMessage startMsg = new StatusMessage(); - startMsg.setBody(publishStatus); - pushMessage(startMsg); - } - - /** - * Sends publish stop notifications - */ - private void sendPublishStopNotify() { - Status stopStatus = new Status(StatusCodes.NS_UNPUBLISHED_SUCCESS); - stopStatus.setClientid(getStreamId()); - stopStatus.setDetails(getPublishedName()); - - StatusMessage stopMsg = new StatusMessage(); - stopMsg.setBody(stopStatus); - pushMessage(stopMsg); - } - - /** - * Sends record failed notifications - */ - private void sendRecordFailedNotify(String reason) { - Status failedStatus = new Status(StatusCodes.NS_RECORD_FAILED); - failedStatus.setLevel(Status.ERROR); - failedStatus.setClientid(getStreamId()); - failedStatus.setDetails(getPublishedName()); - failedStatus.setDesciption(reason); - - StatusMessage failedMsg = new StatusMessage(); - failedMsg.setBody(failedStatus); - pushMessage(failedMsg); - } - - /** - * Sends record start notifications - */ - private void sendRecordStartNotify() { - Status recordStatus = new Status(StatusCodes.NS_RECORD_START); - recordStatus.setClientid(getStreamId()); - recordStatus.setDetails(getPublishedName()); - - StatusMessage startMsg = new StatusMessage(); - startMsg.setBody(recordStatus); - pushMessage(startMsg); - } - - /** - * Sends record stop notifications - */ - private void sendRecordStopNotify() { - Status stopStatus = new Status(StatusCodes.NS_RECORD_STOP); - stopStatus.setClientid(getStreamId()); - stopStatus.setDetails(getPublishedName()); - - StatusMessage stopMsg = new StatusMessage(); - stopMsg.setBody(stopStatus); - pushMessage(stopMsg); - } - - /** - * Pushes a message out to a consumer. - * - * @param msg StatusMessage - */ - protected void pushMessage(StatusMessage msg) { - if (connMsgOut != null) { - try { - connMsgOut.pushMessage(msg); - } catch (IOException err) { - log.error("Error while pushing message: {}", msg, err); - } - } else { - log.warn("Consumer message output is null"); - } - } - - private void sendStartNotifications(IEventListener source) { - if (sendStartNotification) { - // notify handler that stream starts recording/publishing - sendStartNotification = false; - if (source instanceof IConnection) { - IScope scope = ((IConnection) source).getScope(); - if (scope.hasHandler()) { - final Object handler = scope.getHandler(); - if (handler instanceof IStreamAwareScopeHandler) { - if (recordingListener != null && recordingListener.get().isRecording()) { - // callback for record start - ((IStreamAwareScopeHandler) handler).streamRecordStart(this); - } else { - // delete any previously recorded versions of this now "live" stream per - // http://livedocs.adobe.com/flashmediaserver/3.0/hpdocs/help.html?content=00000186.html - try { - File file = getRecordFile(scope, publishedName); - if (file != null && file.exists()) { - if (!file.delete()) { - log.debug("File was not deleted: {}", file.getAbsoluteFile()); - } - } - } catch (Exception e) { - log.warn("Exception removing previously recorded file", e); - } - // callback for publish start - ((IStreamAwareScopeHandler) handler).streamPublishStart(this); - } - } - } - } - // send start notifications - sendPublishStartNotify(); - if (recordingListener != null && recordingListener.get().isRecording()) { - sendRecordStartNotify(); - } - notifyBroadcastStart(); - } - } - - /** - * Starts stream, creates pipes, connects - */ - public void start() { - log.info("Stream start: {}", publishedName); - checkVideoCodec = true; - checkAudioCodec = true; - firstPacketTime = -1; - latestTimeStamp = -1; - bytesReceived = 0; - IConsumerService consumerManager = (IConsumerService) getScope().getContext().getBean(IConsumerService.KEY); - connMsgOut = consumerManager.getConsumerOutput(this); - if (connMsgOut != null && connMsgOut.subscribe(this, null)) { - setCodecInfo(new StreamCodecInfo()); - creationTime = System.currentTimeMillis(); - closed = false; - } else { - log.warn("Subscribe failed"); - } - } - - /** {@inheritDoc} */ - public void startPublishing() { - // We send the start messages before the first packet is received. - // This is required so FME actually starts publishing. - sendStartNotifications(Red5.getConnectionLocal()); - // force recording if set - if (automaticRecording) { - log.debug("Starting automatic recording of {}", publishedName); - try { - saveAs(publishedName, false); - } catch (Exception e) { - log.warn("Start of automatic recording failed", e); - } - } - } - - /** {@inheritDoc} */ - public void stop() { - log.info("Stream stop: {}", publishedName); - stopRecording(); - close(); - } - - /** - * Stops any currently active recording. - */ - public void stopRecording() { - IRecordingListener listener = null; - if (recordingListener != null && (listener = recordingListener.get()).isRecording()) { - sendRecordStopNotify(); - notifyRecordingStop(); - // remove the listener - removeStreamListener(listener); - // stop the recording listener - listener.stop(); - // clear and null-out the thread local - recordingListener.clear(); - recordingListener = null; - } - } - - public boolean isRecording() { - return recordingListener != null && recordingListener.get().isRecording(); - } - - /** {@inheritDoc} */ - public void addStreamListener(IStreamListener listener) { - listeners.add(listener); - } - - /** {@inheritDoc} */ - public Collection getStreamListeners() { - return listeners; - } - - /** {@inheritDoc} */ - public void removeStreamListener(IStreamListener listener) { - listeners.remove(listener); - } - - /** - * Get the file we'd be recording to based on scope and given name. - * - * @param scope - * @param name - * @return file - */ - protected File getRecordFile(IScope scope, String name) { - return RecordingListener.getRecordFile(scope, name); - } - - protected void registerJMX() { - if (registerJMX) { - // register with jmx - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - try { - ObjectName oName = new ObjectName(String.format("org.red5.server:type=ClientBroadcastStream,scope=%s,publishedName=%s", getScope().getName(), publishedName)); - mbs.registerMBean(new StandardMBean(this, ClientBroadcastStreamMXBean.class, true), oName); - } catch (InstanceAlreadyExistsException e) { - log.debug("Instance already registered", e); - } catch (Exception e) { - log.warn("Error on jmx registration", e); - } - } - } - - protected void unregisterJMX() { - if (registerJMX) { - if (StringUtils.isNotEmpty(publishedName) && !"false".equals(publishedName)) { - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - try { - ObjectName oName = new ObjectName(String.format("org.red5.server:type=ClientBroadcastStream,scope=%s,publishedName=%s", getScope().getName(), publishedName)); - mbs.unregisterMBean(oName); - } catch (Exception e) { - log.warn("Exception unregistering", e); - } - } - } - } - -} diff --git a/src/main/java/org/red5/server/stream/DefaultStreamFilenameGenerator.java b/src/main/java/org/red5/server/stream/DefaultStreamFilenameGenerator.java deleted file mode 100644 index e03b3e465..000000000 --- a/src/main/java/org/red5/server/stream/DefaultStreamFilenameGenerator.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import org.red5.server.api.scope.IScope; -import org.red5.server.api.stream.IStreamFilenameGenerator; -import org.red5.server.util.ScopeUtils; - -/** - * Default filename generator for streams. The files will be stored in a - * directory "streams" in the application folder. Option for changing directory - * streams are saved to is investigated as of 0.6RC1. - * - * @author The Red5 Project - * @author Joachim Bauch (bauch@struktur.de) - */ -public class DefaultStreamFilenameGenerator implements IStreamFilenameGenerator { - - /** - * Generate stream directory based on relative scope path. The base directory is - * streams, e.g. a scope /application/one/two/ will - * generate a directory /streams/one/two/ inside the application. - * - * @param scope Scope - * @return Directory based on relative scope path - */ - private String getStreamDirectory(IScope scope) { - final StringBuilder result = new StringBuilder(); - final IScope app = ScopeUtils.findApplication(scope); - final String prefix = "streams/"; - while (scope != null && scope != app) { - result.insert(0, scope.getName() + "/"); - scope = scope.getParent(); - } - if (result.length() == 0) { - return prefix; - } else { - result.insert(0, prefix).append('/'); - return result.toString(); - } - } - - /** {@inheritDoc} */ - public String generateFilename(IScope scope, String name, GenerationType type) { - return generateFilename(scope, name, null, type); - } - - /** {@inheritDoc} */ - public String generateFilename(IScope scope, String name, String extension, GenerationType type) { - String result = getStreamDirectory(scope) + name; - if (extension != null && !extension.equals("")) { - result += extension; - } - return result; - } - - /** - * The default filenames are relative to the scope path, so always return false. - * - * @return always false - */ - public boolean resolvesToAbsolutePath() { - return false; - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/stream/IConsumerService.java b/src/main/java/org/red5/server/stream/IConsumerService.java deleted file mode 100644 index 7641c33f0..000000000 --- a/src/main/java/org/red5/server/stream/IConsumerService.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import org.red5.server.api.stream.IClientStream; -import org.red5.server.messaging.IMessageOutput; - -/** - * Service for consumer objects, used to get pushed messages at consumer endpoint. - */ -public interface IConsumerService { - public static final String KEY = "consumerService"; - - /** - * Handles pushed messages - * - * @param stream Client stream object - * @return Message object - */ - IMessageOutput getConsumerOutput(IClientStream stream); -} diff --git a/src/main/java/org/red5/server/stream/IFrameDropper.java b/src/main/java/org/red5/server/stream/IFrameDropper.java deleted file mode 100644 index 8aa7c2604..000000000 --- a/src/main/java/org/red5/server/stream/IFrameDropper.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import org.red5.server.stream.message.RTMPMessage; - -/** - * Interface for classes that implement logic to drop frames. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public interface IFrameDropper { - - /** Send keyframes, interframes and disposable interframes. */ - public static final int SEND_ALL = 0; - - /** Send keyframes and interframes. */ - public static final int SEND_INTERFRAMES = 1; - - /** Send keyframes only. */ - public static final int SEND_KEYFRAMES = 2; - - /** Send keyframes only and switch to SEND_INTERFRAMES later. */ - public static final int SEND_KEYFRAMES_CHECK = 3; - - /** - * Checks if a message may be sent to the subscriber. - * - * @param message - * the message to check - * @param pending - * the number of pending messages - * @return true if the packet may be sent, otherwise - * false - */ - boolean canSendPacket(RTMPMessage message, long pending); - - /** - * Notify that a packet has been dropped. - * - * @param message - * the message that was dropped - */ - void dropPacket(RTMPMessage message); - - /** - * Notify that a message has been sent. - * - * @param message - * the message that was sent - */ - void sendPacket(RTMPMessage message); - - /** Reset the frame dropper. */ - void reset(); - - /** - * Reset the frame dropper to a given state. - * - * @param state - * the state to reset the frame dropper to - */ - void reset(int state); - -} diff --git a/src/main/java/org/red5/server/stream/IProviderService.java b/src/main/java/org/red5/server/stream/IProviderService.java deleted file mode 100644 index 1b1ee86f1..000000000 --- a/src/main/java/org/red5/server/stream/IProviderService.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.io.File; -import java.util.Set; - -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeService; -import org.red5.server.api.stream.IBroadcastStream; -import org.red5.server.messaging.IMessageInput; - -/** - * Central unit to get access to different types of provider inputs - */ -public interface IProviderService extends IScopeService { - - public final static String BEAN_NAME = "providerService"; - - enum INPUT_TYPE { - NOT_FOUND, LIVE, LIVE_WAIT, VOD; - }; - - /** - * Returns the input type for a named provider if a source of input exists. - * Live is checked first and VOD second. - * - * @param scope Scope of provider - * @param name Name of provider - * @param type Type of video stream - * @return LIVE if live, VOD if VOD, and NOT_FOUND otherwise - */ - INPUT_TYPE lookupProviderInput(IScope scope, String name, int type); - - /** - * Get a named provider as the source of input. - * Live stream first, VOD stream second. - * @param scope Scope of provider - * @param name Name of provider - * @return null if nothing found. - */ - IMessageInput getProviderInput(IScope scope, String name); - - /** - * Get a named Live provider as the source of input. - * - * @param scope Scope of provider - * @param name Name of provider - * @param needCreate Whether there's need to create basic scope / live provider if they don't exist - * @return null if not found. - */ - IMessageInput getLiveProviderInput(IScope scope, String name, boolean needCreate); - - /** - * Get a named VOD provider as the source of input. - * - * @param scope Scope of provider - * @param name Name of provider - * @return null if not found. - */ - IMessageInput getVODProviderInput(IScope scope, String name); - - /** - * Get a named VOD source file. - * - * @param scope Scope of provider - * @param name Name of provider - * @return null if not found. - */ - File getVODProviderFile(IScope scope, String name); - - /** - * Register a broadcast stream to a scope. - * - * @param scope Scope - * @param name Name of stream - * @param stream Broadcast stream to register - * @return true if register successfully. - */ - boolean registerBroadcastStream(IScope scope, String name, IBroadcastStream stream); - - /** - * Get names of existing broadcast streams in a scope. - * - * @param scope Scope to get stream names from - * @return List of stream names - */ - Set getBroadcastStreamNames(IScope scope); - - /** - * Unregister a broadcast stream of a specific name from a scope. - * - * @param scope Scope - * @param name Stream name - * @return true if unregister successfully. - */ - boolean unregisterBroadcastStream(IScope scope, String name); - - /** - * Unregister a broadcast stream of a specific name from a scope. - * - * @param scope Scope - * @param name Stream name - * @param stream Broadcast stream - * @return true if unregister successfully. - */ - boolean unregisterBroadcastStream(IScope scope, String name, IBroadcastStream stream); - -} diff --git a/src/main/java/org/red5/server/stream/IRecordingListener.java b/src/main/java/org/red5/server/stream/IRecordingListener.java deleted file mode 100644 index 689e4b7f3..000000000 --- a/src/main/java/org/red5/server/stream/IRecordingListener.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.red5.server.stream; - -import org.red5.server.api.IConnection; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.stream.IBroadcastStream; -import org.red5.server.api.stream.IStreamListener; -import org.red5.server.api.stream.IStreamPacket; -import org.red5.server.stream.consumer.FileConsumer; - -/** - * Recording listener interface. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public interface IRecordingListener extends IStreamListener { - - /** - * Initialize the listener. - * - * @param conn Stream source connection - * @param name Stream name - * @param isAppend Append mode - * @return true if initialization completes and false otherwise - */ - public boolean init(IConnection conn, String name, boolean isAppend); - - /** - * Initialize the listener. - * - * @param scope Stream source scope - * @param name Stream name - * @param isAppend Append mode - * @return true if initialization completes and false otherwise - */ - public boolean init(IScope scope, String name, boolean isAppend); - - /** - * Start the recording. - */ - public void start(); - - /** - * Stop the recording. - */ - public void stop(); - - /** {@inheritDoc} */ - public void packetReceived(IBroadcastStream stream, IStreamPacket packet); - - /** - * @return recording state, true if recording and false otherwise - */ - public boolean isRecording(); - - /** - * @return appending state, true if appending and false otherwise - */ - public boolean isAppending(); - - /** - * @return the recordingConsumer - */ - public FileConsumer getFileConsumer(); - - /** - * @param recordingConsumer the recordingConsumer to set - */ - public void setFileConsumer(FileConsumer recordingConsumer); - - /** - * @return the fileName - */ - public String getFileName(); - - /** - * @param fileName the fileName to set - */ - public void setFileName(String fileName); - -} diff --git a/src/main/java/org/red5/server/stream/ISeekableProvider.java b/src/main/java/org/red5/server/stream/ISeekableProvider.java deleted file mode 100644 index d348ba6ea..000000000 --- a/src/main/java/org/red5/server/stream/ISeekableProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import org.red5.server.messaging.IProvider; - -/** - * Provider that is seekable - */ -public interface ISeekableProvider extends IProvider { - public static final String KEY = ISeekableProvider.class.getName(); - - /** - * Seek the provider to timestamp ts (in milliseconds). - * @param ts Timestamp to seek to - * @return Actual timestamp seeked to - */ - int seek(int ts); -} diff --git a/src/main/java/org/red5/server/stream/IStreamData.java b/src/main/java/org/red5/server/stream/IStreamData.java deleted file mode 100644 index fd55bc44f..000000000 --- a/src/main/java/org/red5/server/stream/IStreamData.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.io.IOException; - -import org.apache.mina.core.buffer.IoBuffer; - -/** - * Stream data packet - */ -public interface IStreamData { - - /** - * Getter for property 'data'. - * - * @return Value for property 'data'. - */ - public IoBuffer getData(); - - /** - * Creates a byte accurate copy. - * - * @return duplicate of the current data item - * @throws IOException - * @throws ClassNotFoundException - */ - public IStreamData duplicate() throws IOException, ClassNotFoundException; - -} diff --git a/src/main/java/org/red5/server/stream/IStreamTypeAwareProvider.java b/src/main/java/org/red5/server/stream/IStreamTypeAwareProvider.java deleted file mode 100644 index bc949ab71..000000000 --- a/src/main/java/org/red5/server/stream/IStreamTypeAwareProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import org.red5.server.messaging.IProvider; - -/** - * Interface for providers that know if they contain video frames. - * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - * - */ -public interface IStreamTypeAwareProvider extends IProvider { - - public static final String KEY = IStreamTypeAwareProvider.class.getName(); - - /** - * Check if the provider contains video tags. - * - * @return provider has video - */ - public boolean hasVideo(); - -} diff --git a/src/main/java/org/red5/server/stream/OutputStream.java b/src/main/java/org/red5/server/stream/OutputStream.java deleted file mode 100644 index 1d0a50843..000000000 --- a/src/main/java/org/red5/server/stream/OutputStream.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import org.red5.server.net.rtmp.Channel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Output stream that consists of audio, video and data channels - * - * @see org.red5.server.net.rtmp.Channel - */ -public class OutputStream { - /** - * Logger - */ - protected static Logger log = LoggerFactory.getLogger(OutputStream.class); - - /** - * Video channel - */ - private Channel video; - - /** - * Audio channel - */ - private Channel audio; - - /** - * Data channel - */ - private Channel data; - - /** - * Creates output stream from channels - * - * @param video Video channel - * @param audio Audio channel - * @param data Data channel - */ - public OutputStream(Channel video, Channel audio, Channel data) { - this.video = video; - this.audio = audio; - this.data = data; - } - - /** - * Closes audion, video and data channels - */ - public void close() { - video.close(); - audio.close(); - data.close(); - } - - /** - * Getter for audio channel - * - * @return Audio channel - */ - public Channel getAudio() { - return audio; - } - - /** - * Getter for data channel - * - * @return Data channel - */ - public Channel getData() { - return data; - } - - /** - * Getter for video channel - * - * @return Video channel - */ - public Channel getVideo() { - return video; - } -} diff --git a/src/main/java/org/red5/server/stream/PlayEngine.java b/src/main/java/org/red5/server/stream/PlayEngine.java deleted file mode 100644 index 756c195e8..000000000 --- a/src/main/java/org/red5/server/stream/PlayEngine.java +++ /dev/null @@ -1,1869 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.codec.IAudioStreamCodec; -import org.red5.codec.IStreamCodecInfo; -import org.red5.codec.IVideoStreamCodec; -import org.red5.codec.StreamCodecInfo; -import org.red5.io.amf.Output; -import org.red5.logging.Red5LoggerFactory; -import org.red5.server.api.scheduling.IScheduledJob; -import org.red5.server.api.scheduling.ISchedulingService; -import org.red5.server.api.scope.IBroadcastScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.stream.IBroadcastStream; -import org.red5.server.api.stream.IPlayItem; -import org.red5.server.api.stream.IPlaylistSubscriberStream; -import org.red5.server.api.stream.ISubscriberStream; -import org.red5.server.api.stream.OperationNotSupportedException; -import org.red5.server.api.stream.StreamState; -import org.red5.server.api.stream.support.DynamicPlayItem; -import org.red5.server.messaging.AbstractMessage; -import org.red5.server.messaging.IConsumer; -import org.red5.server.messaging.IFilter; -import org.red5.server.messaging.IMessage; -import org.red5.server.messaging.IMessageComponent; -import org.red5.server.messaging.IMessageInput; -import org.red5.server.messaging.IMessageOutput; -import org.red5.server.messaging.IPassive; -import org.red5.server.messaging.IPipe; -import org.red5.server.messaging.IPipeConnectionListener; -import org.red5.server.messaging.IProvider; -import org.red5.server.messaging.IPushableConsumer; -import org.red5.server.messaging.InMemoryPushPushPipe; -import org.red5.server.messaging.OOBControlMessage; -import org.red5.server.messaging.PipeConnectionEvent; -import org.red5.server.net.rtmp.event.Aggregate; -import org.red5.server.net.rtmp.event.AudioData; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.Ping; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.net.rtmp.event.VideoData.FrameType; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.net.rtmp.status.Status; -import org.red5.server.net.rtmp.status.StatusCodes; -import org.red5.server.net.rtmpt.RTMPTConnection; -import org.red5.server.stream.message.RTMPMessage; -import org.red5.server.stream.message.ResetMessage; -import org.red5.server.stream.message.StatusMessage; -import org.slf4j.Logger; - -/** - * A play engine for playing an IPlayItem. - * - * @author The Red5 Project - * @author Steven Gong - * @author Paul Gregoire (mondain@gmail.com) - * @author Dan Rossi - * @author Tiago Daniel Jacobs (tiago@imdt.com.br) - * @author Vladimir Hmelyoff (vlhm@splitmedialabs.com) - */ -public final class PlayEngine implements IFilter, IPushableConsumer, IPipeConnectionListener { - - private static final Logger log = Red5LoggerFactory.getLogger(PlayEngine.class); - - private IMessageInput msgIn; - - private IMessageOutput msgOut; - - private final ISubscriberStream subscriberStream; - - private ISchedulingService schedulingService; - - private IConsumerService consumerService; - - private IProviderService providerService; - - private int streamId; - - /** - * Receive video? - */ - private boolean receiveVideo = true; - - /** - * Receive audio? - */ - private boolean receiveAudio = true; - - private boolean pullMode; - - private String waitLiveJob; - - private boolean waiting; - - /** - * timestamp of first sent packet - */ - private int streamStartTS; - - private IPlayItem currentItem; - - private RTMPMessage pendingMessage; - - /** - * Interval in ms to check for buffer underruns in VOD streams. - */ - private int bufferCheckInterval = 0; - - /** - * Number of pending messages at which a NetStream.Play.InsufficientBW - * message is generated for VOD streams. - */ - private int underrunTrigger = 10; - - /** - * threshold for number of pending video frames - */ - private int maxPendingVideoFramesThreshold = 10; - - /** - * if we have more than 1 pending video frames, but less than maxPendingVideoFrames, - * continue sending until there are this many sequential frames with more than 1 pending - */ - private int maxSequentialPendingVideoFrames = 10; - - /** - * the number of sequential video frames with > 0 pending frames - */ - private int numSequentialPendingVideoFrames = 0; - - /** - * State machine for video frame dropping in live streams - */ - private IFrameDropper videoFrameDropper = new VideoFrameDropper(); - - private int timestampOffset = 0; - - /** - * Timestamp of the last message sent to the client. - */ - private int lastMessageTs = -1; - - /** - * Number of bytes sent. - */ - private AtomicLong bytesSent = new AtomicLong(0); - - /** - * Start time of stream playback. - * It's not a time when the stream is being played but the time when the stream should be played if it's played - * from the very beginning. - * Eg. A stream is played at timestamp 5s on 1:00:05. The playbackStart is 1:00:00. - */ - private volatile long playbackStart; - - /** - * Flag denoting whether or not the push and pull job is scheduled. The job makes sure messages are sent to the client. - */ - private volatile String pullAndPush; - - /** - * Flag denoting whether or not the job that closes stream after buffer runs out is scheduled. - */ - private volatile String deferredStop; - - /** - * Monitor guarding completion of a given push/pull run. - * Used to wait for job cancellation to finish. - */ - private final AtomicBoolean pushPullRunning = new AtomicBoolean(false); - - /** - * Offset in milliseconds where the stream started. - */ - private int streamOffset; - - /** - * Timestamp when buffer should be checked for underruns next. - */ - private long nextCheckBufferUnderrun; - - /** - * Send blank audio packet next? - */ - private boolean sendBlankAudio; - - /** - * decision: 0 for Live, 1 for File, 2 for Wait, 3 for N/A - */ - private int playDecision = 3; - - /** - * List of pending operations - */ - private ConcurrentLinkedQueue pendingOperations; - - /** - * Constructs a new PlayEngine. - */ - private PlayEngine(Builder builder) { - subscriberStream = builder.subscriberStream; - schedulingService = builder.schedulingService; - consumerService = builder.consumerService; - providerService = builder.providerService; - // get the stream id - streamId = subscriberStream.getStreamId(); - // create pending operation list - pendingOperations = new ConcurrentLinkedQueue(); - } - - /** - * Builder pattern - */ - public final static class Builder { - //Required for play engine - private ISubscriberStream subscriberStream; - - //Required for play engine - private ISchedulingService schedulingService; - - //Required for play engine - private IConsumerService consumerService; - - //Required for play engine - private IProviderService providerService; - - public Builder(ISubscriberStream subscriberStream, ISchedulingService schedulingService, IConsumerService consumerService, IProviderService providerService) { - this.subscriberStream = subscriberStream; - this.schedulingService = schedulingService; - this.consumerService = consumerService; - this.providerService = providerService; - } - - public PlayEngine build() { - return new PlayEngine(this); - } - - } - - public void setBufferCheckInterval(int bufferCheckInterval) { - this.bufferCheckInterval = bufferCheckInterval; - } - - public void setUnderrunTrigger(int underrunTrigger) { - this.underrunTrigger = underrunTrigger; - } - - void setMessageOut(IMessageOutput msgOut) { - this.msgOut = msgOut; - } - - /** - * Start stream - */ - public void start() { - switch (subscriberStream.getState()) { - case UNINIT: - // allow start if uninitialized - // change state to stopped - subscriberStream.setState(StreamState.STOPPED); - if (msgOut == null) { - msgOut = consumerService.getConsumerOutput(subscriberStream); - msgOut.subscribe(this, null); - } - break; - default: - throw new IllegalStateException(String.format("Cannot start in current state: %s", subscriberStream.getState())); - } - } - - /** - * Play stream - * @param item Playlist item - * @throws StreamNotFoundException Stream not found - * @throws IllegalStateException Stream is in stopped state - * @throws IOException Stream had io exception - */ - public void play(IPlayItem item) throws StreamNotFoundException, IllegalStateException, IOException { - play(item, true); - } - - /** - * Play stream - * @param item Playlist item - * @param withReset Send reset status before playing. - * @throws StreamNotFoundException Stream not found - * @throws IllegalStateException Stream is in stopped state - * @throws IOException Stream had IO exception - */ - public void play(IPlayItem item, boolean withReset) throws StreamNotFoundException, IllegalStateException, IOException { - // cannot play if state is not stopped - switch (subscriberStream.getState()) { - case STOPPED: - //allow play if stopped - if (msgIn != null) { - msgIn.unsubscribe(this); - msgIn = null; - } - break; - default: - throw new IllegalStateException("Cannot play from non-stopped state"); - } - // Play type determination - // http://livedocs.adobe.com/flex/3/langref/flash/net/NetStream.html#play%28%29 - // The start time, in seconds. Allowed values are -2, -1, 0, or a positive number. - // The default value is -2, which looks for a live stream, then a recorded stream, - // and if it finds neither, opens a live stream. - // If -1, plays only a live stream. - // If 0 or a positive number, plays a recorded stream, beginning start seconds in. - // - // -2: live then recorded, -1: live, >=0: recorded - int type = (int) (item.getStart() / 1000); - log.debug("Type {}", type); - // see if it's a published stream - IScope thisScope = subscriberStream.getScope(); - final String itemName = item.getName(); - //check for input and type - IProviderService.INPUT_TYPE sourceType = providerService.lookupProviderInput(thisScope, itemName, type); - - boolean isPublishedStream = sourceType == IProviderService.INPUT_TYPE.LIVE; - boolean isPublishedStreamWait = sourceType == IProviderService.INPUT_TYPE.LIVE_WAIT; - boolean isFileStream = sourceType == IProviderService.INPUT_TYPE.VOD; - - boolean sendNotifications = true; - - // decision: 0 for Live, 1 for File, 2 for Wait, 3 for N/A - switch (type) { - case -2: - if (isPublishedStream) { - playDecision = 0; - } else if (isFileStream) { - playDecision = 1; - } else if (isPublishedStreamWait) { - playDecision = 2; - } - break; - case -1: - if (isPublishedStream) { - playDecision = 0; - } else { - playDecision = 2; - } - break; - default: - if (isFileStream) { - playDecision = 1; - } - break; - } - log.debug("Play decision is {} (0=Live, 1=File, 2=Wait, 3=N/A)", playDecision); - IMessage msg = null; - currentItem = item; - long itemLength = item.getLength(); - log.debug("Item length: {}", itemLength); - switch (playDecision) { - case 0: - //get source input without create - msgIn = providerService.getLiveProviderInput(thisScope, itemName, false); - if (msgIn == null) { - sendStreamNotFoundStatus(currentItem); - throw new StreamNotFoundException(itemName); - } else { - //drop all frames up to the next keyframe - videoFrameDropper.reset(IFrameDropper.SEND_KEYFRAMES_CHECK); - if (msgIn instanceof IBroadcastScope) { - IBroadcastStream stream = (IBroadcastStream) ((IBroadcastScope) msgIn).getClientBroadcastStream(); - if (stream != null && stream.getCodecInfo() != null) { - IVideoStreamCodec videoCodec = stream.getCodecInfo().getVideoCodec(); - if (videoCodec != null) { - if (withReset) { - sendReset(); - sendResetStatus(item); - sendStartStatus(item); - } - sendNotifications = false; - } - } - } - //Subscribe to stream (ClientBroadcastStream.onPipeConnectionEvent) - if (msgIn != null) { - msgIn.subscribe(this, null); - //execute the processes to get Live playback setup - playLive(); - } else { - sendStreamNotFoundStatus(currentItem); - throw new StreamNotFoundException(itemName); - } - } - break; - case 2: - //get source input with create - msgIn = providerService.getLiveProviderInput(thisScope, itemName, true); - msgIn.subscribe(this, null); - waiting = true; - if (type == -1 && itemLength >= 0) { - log.debug("Creating wait job"); - // Wait given timeout for stream to be published - waitLiveJob = schedulingService.addScheduledOnceJob(itemLength, new IScheduledJob() { - public void execute(ISchedulingService service) { - //set the msgIn if its null - if (msgIn == null) { - connectToProvider(itemName); - } - waitLiveJob = null; - waiting = false; - subscriberStream.onChange(StreamState.END); - } - }); - } else if (type == -2) { - log.debug("Creating wait job"); - // Wait x seconds for the stream to be published - waitLiveJob = schedulingService.addScheduledOnceJob(15000, new IScheduledJob() { - public void execute(ISchedulingService service) { - //set the msgIn if its null - if (msgIn == null) { - connectToProvider(itemName); - } - waitLiveJob = null; - waiting = false; - } - }); - } else { - connectToProvider(itemName); - } - break; - case 1: - msgIn = providerService.getVODProviderInput(thisScope, itemName); - if (msgIn == null) { - sendStreamNotFoundStatus(currentItem); - throw new StreamNotFoundException(itemName); - } else if (msgIn.subscribe(this, null)) { - //execute the processes to get VOD playback setup - msg = playVOD(withReset, itemLength); - } else { - log.error("Input source subscribe failed"); - throw new IOException(String.format("Subscribe to %s failed", itemName)); - } - break; - default: - sendStreamNotFoundStatus(currentItem); - throw new StreamNotFoundException(itemName); - } - //continue with common play processes (live and vod) - if (sendNotifications) { - if (withReset) { - sendReset(); - sendResetStatus(item); - } - sendStartStatus(item); - if (!withReset) { - sendSwitchStatus(); - } - // if its dynamic playback send the complete status - if (item instanceof DynamicPlayItem) { - sendTransitionStatus(); - } - } - if (msg != null) { - sendMessage((RTMPMessage) msg); - } - subscriberStream.onChange(StreamState.PLAYING, currentItem, !pullMode); - if (withReset) { - long currentTime = System.currentTimeMillis(); - playbackStart = currentTime - streamOffset; - nextCheckBufferUnderrun = currentTime + bufferCheckInterval; - if (currentItem.getLength() != 0) { - ensurePullAndPushRunning(); - } - } - } - - /** - * Performs the processes needed for live streams. - * The following items are sent if they exist: - * - Metadata - * - Decoder configurations (ie. AVC codec) - * - Most recent keyframe - * - * @throws IOException - */ - private final void playLive() throws IOException { - //change state - subscriberStream.setState(StreamState.PLAYING); - streamOffset = 0; - streamStartTS = -1; - if (msgIn != null && msgOut != null) { - // get the stream so that we can grab any metadata and decoder configs - IBroadcastStream stream = (IBroadcastStream) ((IBroadcastScope) msgIn).getClientBroadcastStream(); - // prevent an NPE when a play list is created and then immediately flushed - if (stream != null) { - Notify metaData = stream.getMetaData(); - //check for metadata to send - if (metaData != null) { - log.debug("Metadata is available"); - RTMPMessage metaMsg = RTMPMessage.build(metaData, 0); - try { - msgOut.pushMessage(metaMsg); - } catch (IOException e) { - log.warn("Error sending metadata", e); - } - } else { - log.debug("No metadata available"); - } - - IStreamCodecInfo codecInfo = stream.getCodecInfo(); - log.debug("Codec info: {}", codecInfo); - if (codecInfo instanceof StreamCodecInfo) { - StreamCodecInfo info = (StreamCodecInfo) codecInfo; - IVideoStreamCodec videoCodec = info.getVideoCodec(); - log.debug("Video codec: {}", videoCodec); - if (videoCodec != null) { - //check for decoder configuration to send - IoBuffer config = videoCodec.getDecoderConfiguration(); - if (config != null) { - log.debug("Decoder configuration is available for {}", videoCodec.getName()); - //log.debug("Dump:\n{}", Hex.encodeHex(config.array())); - VideoData conf = new VideoData(config.asReadOnlyBuffer()); - log.trace("Configuration ts: {}", conf.getTimestamp()); - RTMPMessage confMsg = RTMPMessage.build(conf); - try { - log.debug("Pushing decoder configuration"); - msgOut.pushMessage(confMsg); - } finally { - conf.release(); - } - } - //check for a keyframe to send - IoBuffer keyFrame = videoCodec.getKeyframe(); - if (keyFrame != null) { - log.debug("Keyframe is available"); - VideoData video = new VideoData(keyFrame.asReadOnlyBuffer()); - log.trace("Keyframe ts: {}", video.getTimestamp()); - //log.debug("Dump:\n{}", Hex.encodeHex(keyFrame.array())); - RTMPMessage videoMsg = RTMPMessage.build(video); - try { - log.debug("Pushing keyframe"); - msgOut.pushMessage(videoMsg); - } finally { - video.release(); - } - } - } else { - log.debug("Could not initialize stream output, videoCodec is null"); - } - // SplitmediaLabs - begin AAC fix - IAudioStreamCodec audioCodec = info.getAudioCodec(); - log.debug("Audio codec: {}", audioCodec); - if (audioCodec != null) { - // check for decoder configuration to send - IoBuffer config = audioCodec.getDecoderConfiguration(); - if (config != null) { - log.debug("Decoder configuration is available for {}", audioCodec.getName()); - //log.debug("Dump:\n{}", Hex.encodeHex(config.array())); - AudioData conf = new AudioData(config.asReadOnlyBuffer()); - log.trace("Configuration ts: {}", conf.getTimestamp()); - RTMPMessage confMsg = RTMPMessage.build(conf); - try { - log.debug("Pushing decoder configuration"); - msgOut.pushMessage(confMsg); - } finally { - conf.release(); - } - } - } else { - log.debug("No decoder configuration available, audioCodec is null"); - } - } - } - } else { - throw new IOException(String.format("A message pipe is null - in: %b out: %b", (msgIn == null), (msgOut == null))); - } - } - - /** - * Performs the processes needed for VOD / pre-recorded streams. - * - * @param withReset whether or not to perform reset on the stream - * @param itemLength length of the item to be played - * @return message for the consumer - * @throws IOException - */ - private final IMessage playVOD(boolean withReset, long itemLength) throws IOException { - IMessage msg = null; - //change state - subscriberStream.setState(StreamState.PLAYING); - streamOffset = 0; - streamStartTS = -1; - if (withReset) { - releasePendingMessage(); - } - sendVODInitCM(msgIn, currentItem); - // Don't use pullAndPush to detect IOExceptions prior to sending - // NetStream.Play.Start - if (currentItem.getStart() > 0) { - streamOffset = sendVODSeekCM(msgIn, (int) currentItem.getStart()); - // We seeked to the nearest keyframe so use real timestamp now - if (streamOffset == -1) { - streamOffset = (int) currentItem.getStart(); - } - } - msg = msgIn.pullMessage(); - if (msg instanceof RTMPMessage) { - // Only send first video frame - IRTMPEvent body = ((RTMPMessage) msg).getBody(); - if (itemLength == 0) { - while (body != null && !(body instanceof VideoData)) { - msg = msgIn.pullMessage(); - if (msg != null && msg instanceof RTMPMessage) { - body = ((RTMPMessage) msg).getBody(); - } else { - break; - } - } - } - if (body != null) { - // Adjust timestamp when playing lists - body.setTimestamp(body.getTimestamp() + timestampOffset); - } - } - return msg; - } - - /** - * Connects to the data provider. - * - * @param itemName name of the item to play - */ - private final void connectToProvider(String itemName) { - log.debug("Attempting connection to {}", itemName); - IScope thisScope = subscriberStream.getScope(); - msgIn = providerService.getLiveProviderInput(thisScope, itemName, true); - if (msgIn != null) { - log.debug("Provider: {}", msgIn); - if (msgIn.subscribe(this, null)) { - log.debug("Subscribed to {} provider", itemName); - //execute the processes to get Live playback setup - try { - playLive(); - } catch (IOException e) { - log.warn("Could not play live stream: {}", itemName, e); - } - } else { - log.warn("Subscribe to {} provider failed", itemName); - } - } else { - log.warn("Provider was not found for {}", itemName); - StreamService.sendNetStreamStatus(subscriberStream.getConnection(), StatusCodes.NS_PLAY_STREAMNOTFOUND, "Stream was not found", itemName, Status.ERROR, streamId); - } - } - - /** - * Pause at position - * - * @param position Position in file - * @throws IllegalStateException If stream is stopped - */ - public void pause(int position) throws IllegalStateException { - switch (subscriberStream.getState()) { - // allow pause if playing or stopped - case PLAYING: - case STOPPED: - subscriberStream.setState(StreamState.PAUSED); - clearWaitJobs(); - sendClearPing(); - sendPauseStatus(currentItem); - subscriberStream.onChange(StreamState.PAUSED, currentItem, position); - break; - default: - throw new IllegalStateException("Cannot pause in current state"); - } - } - - /** - * Resume playback - * - * @param position Resumes playback - * @throws IllegalStateException If stream is stopped - */ - public void resume(int position) throws IllegalStateException { - switch (subscriberStream.getState()) { - // allow resume from pause - case PAUSED: - subscriberStream.setState(StreamState.PLAYING); - sendReset(); - sendResumeStatus(currentItem); - if (pullMode) { - sendVODSeekCM(msgIn, position); - subscriberStream.onChange(StreamState.RESUMED, currentItem, position); - playbackStart = System.currentTimeMillis() - position; - if (currentItem.getLength() >= 0 && (position - streamOffset) >= currentItem.getLength()) { - // Resume after end of stream - stop(); - } else { - ensurePullAndPushRunning(); - } - } else { - subscriberStream.onChange(StreamState.RESUMED, currentItem, position); - videoFrameDropper.reset(VideoFrameDropper.SEND_KEYFRAMES_CHECK); - } - break; - default: - throw new IllegalStateException("Cannot resume from non-paused state"); - } - } - - /** - * Seek to a given position - * - * @param position Position - * @throws IllegalStateException If stream is in stopped state - * @throws OperationNotSupportedException If this object doesn't support the operation. - */ - public void seek(int position) throws IllegalStateException, OperationNotSupportedException { - // add this pending seek operation to the list - pendingOperations.add(new SeekRunnable(position)); - cancelDeferredStop(); - } - - /** - * Stop playback - * - * @throws IllegalStateException If stream is in stopped state - */ - public void stop() throws IllegalStateException { - switch (subscriberStream.getState()) { - // allow stop if playing or paused - case PLAYING: - case PAUSED: - subscriberStream.setState(StreamState.STOPPED); - if (msgIn != null && !pullMode) { - msgIn.unsubscribe(this); - msgIn = null; - } - subscriberStream.onChange(StreamState.STOPPED, currentItem); - clearWaitJobs(); - cancelDeferredStop(); - if (subscriberStream instanceof IPlaylistSubscriberStream) { - IPlaylistSubscriberStream pss = (IPlaylistSubscriberStream) subscriberStream; - if (!pss.hasMoreItems()) { - releasePendingMessage(); - sendCompleteStatus(); - bytesSent.set(0); - sendClearPing(); - sendStopStatus(currentItem); - } else { - if (lastMessageTs > 0) { - // remember last timestamp so we can generate correct headers in playlists. - timestampOffset = lastMessageTs; - } - pss.nextItem(); - } - } - break; - case CLOSED: - clearWaitJobs(); - if (deferredStop != null) { - subscriberStream.cancelJob(deferredStop); - deferredStop = null; - } - default: - throw new IllegalStateException(String.format("Cannot stop in current state: %s", subscriberStream.getState())); - } - // once we've stopped there's no need for the deferred job - if (deferredStop != null) { - subscriberStream.cancelJob(deferredStop); - } - } - - /** - * Close stream - */ - public void close() { - if (!subscriberStream.getState().equals(StreamState.CLOSED)) { - if (msgIn != null) { - msgIn.unsubscribe(this); - msgIn = null; - } - subscriberStream.setState(StreamState.CLOSED); - clearWaitJobs(); - releasePendingMessage(); - lastMessageTs = 0; - // XXX is clear ping required? - //sendClearPing(); - if (msgOut != null) { - List consumers = ((InMemoryPushPushPipe) msgOut).getConsumers(); - // i would assume a list of 1 in most cases - if (!consumers.isEmpty()) { - log.debug("Message out consumers: {}", consumers.size()); - for (IConsumer consumer : consumers) { - ((InMemoryPushPushPipe) msgOut).unsubscribe(consumer); - } - } - msgOut = null; - } - } else { - log.debug("Stream is already in closed state"); - } - } - - /** - * Check if it's okay to send the client more data. This takes the configured - * bandwidth as well as the requested client buffer into account. - * - * @param message - * @return true if it is ok to send more, false otherwise - */ - private boolean okayToSendMessage(IRTMPEvent message) { - if (message instanceof IStreamData) { - final long now = System.currentTimeMillis(); - // check client buffer size - if (isClientBufferFull(now)) { - return false; - } - // get pending message count - long pending = pendingMessages(); - if (bufferCheckInterval > 0 && now >= nextCheckBufferUnderrun) { - if (pending > underrunTrigger) { - // client is playing behind speed, notify him - sendInsufficientBandwidthStatus(currentItem); - } - nextCheckBufferUnderrun = now + bufferCheckInterval; - } - // check for under run - if (pending > underrunTrigger) { - // too many messages already queued on the connection - return false; - } - return true; - } else { - String itemName = "Undefined"; - // if current item exists get the name to help debug this issue - if (currentItem != null) { - itemName = currentItem.getName(); - } - Object[] errorItems = new Object[] { message.getClass(), message.getDataType(), itemName }; - throw new RuntimeException(String.format("Expected IStreamData but got %s (type %s) for %s", errorItems)); - } - } - - /** - * Estimate client buffer fill. - * @param now The current timestamp being used. - * @return True if it appears that the client buffer is full, otherwise false. - */ - private boolean isClientBufferFull(final long now) { - // check client buffer length when we've already sent some messages - if (lastMessageTs > 0) { - // duration the stream is playing / playback duration - final long delta = now - playbackStart; - // buffer size as requested by the client - final long buffer = subscriberStream.getClientBufferDuration(); - // expected amount of data present in client buffer - final long buffered = lastMessageTs - delta; - log.trace("isClientBufferFull: timestamp {} delta {} buffered {} buffer duration {}", new Object[] { lastMessageTs, delta, buffered, buffer }); - // fix for SN-122, this sends double the size of the client buffer - if (buffer > 0 && buffered > (buffer * 2)) { - // client is likely to have enough data in the buffer - return true; - } - } - return false; - } - - private boolean isClientBufferEmpty() { - // check client buffer length when we've already sent some messages - if (lastMessageTs >= 0) { - // duration the stream is playing / playback duration - final long delta = System.currentTimeMillis() - playbackStart; - // expected amount of data present in client buffer - final long buffered = lastMessageTs - delta; - log.trace("isClientBufferEmpty: timestamp {} delta {} buffered {}", new Object[] { lastMessageTs, delta, buffered }); - if (buffered < 0) { - return true; - } - } - return false; - } - - /** - * Make sure the pull and push processing is running. - */ - private void ensurePullAndPushRunning() { - log.trace("State should be PLAYING to running this task: {}", subscriberStream.getState()); - if (pullMode && pullAndPush == null && subscriberStream.getState() == StreamState.PLAYING) { - // client buffer is at least 100ms - pullAndPush = subscriberStream.scheduleWithFixedDelay(new PullAndPushRunnable(), 10); - } - } - - /** - * Clear all scheduled waiting jobs - */ - private void clearWaitJobs() { - log.debug("Clear wait jobs"); - if (pullAndPush != null) { - subscriberStream.cancelJob(pullAndPush); - releasePendingMessage(); - pullAndPush = null; - } - if (waitLiveJob != null) { - schedulingService.removeScheduledJob(waitLiveJob); - waitLiveJob = null; - } - } - - /** - * Sends a status message. - * - * @param status - */ - private void doPushMessage(Status status) { - StatusMessage message = new StatusMessage(); - message.setBody(status); - doPushMessage(message); - } - - /** - * Send message to output stream and handle exceptions. - * - * @param message The message to send. - */ - private void doPushMessage(AbstractMessage message) { - log.trace("doPushMessage: {}", message.getMessageType()); - if (msgOut != null) { - try { - msgOut.pushMessage(message); - if (message instanceof RTMPMessage) { - IRTMPEvent body = ((RTMPMessage) message).getBody(); - //update the last message sent's timestamp - lastMessageTs = body.getTimestamp(); - IoBuffer streamData = null; - if (body instanceof IStreamData && (streamData = ((IStreamData) body).getData()) != null) { - bytesSent.addAndGet(streamData.limit()); - } - } - } catch (IOException err) { - log.error("Error while pushing message", err); - } - } else { - log.warn("Push message failed due to null output pipe"); - } - } - - /** - * Send RTMP message - * @param message RTMP message - */ - private void sendMessage(RTMPMessage messageIn) { - IRTMPEvent event; - IoBuffer dataReference; - switch (messageIn.getBody().getDataType()) { - case Constants.TYPE_AGGREGATE: - dataReference = ((Aggregate) messageIn.getBody()).getData(); - event = new Aggregate(dataReference); - event.setTimestamp(messageIn.getBody().getTimestamp()); - break; - case Constants.TYPE_AUDIO_DATA: - dataReference = ((AudioData) messageIn.getBody()).getData(); - event = new AudioData(dataReference); - event.setTimestamp(messageIn.getBody().getTimestamp()); - break; - case Constants.TYPE_VIDEO_DATA: - dataReference = ((VideoData) messageIn.getBody()).getData(); - event = new VideoData(dataReference); - event.setTimestamp(messageIn.getBody().getTimestamp()); - break; - default: - dataReference = ((Notify) messageIn.getBody()).getData(); - event = new Notify(dataReference); - event.setTimestamp(messageIn.getBody().getTimestamp()); - break; - } - RTMPMessage messageOut = RTMPMessage.build(event); - //get the current timestamp from the message - int ts = messageOut.getBody().getTimestamp(); - if (log.isTraceEnabled()) { - log.trace("sendMessage: streamStartTS={}, length={}, streamOffset={}, timestamp={}", new Object[] { streamStartTS, currentItem.getLength(), streamOffset, ts }); - final long delta = System.currentTimeMillis() - playbackStart; - log.trace("clientBufferDetails: timestamp {} delta {} buffered {}", new Object[] { lastMessageTs, delta, lastMessageTs - delta }); - } - // don't reset streamStartTS to 0 for live streams - if ((streamStartTS == -1 && (ts > 0 || playDecision != 0)) || streamStartTS > ts) { - log.debug("sendMessage: resetting streamStartTS"); - streamStartTS = ts; - messageOut.getBody().setTimestamp(0); - } - //relative timestamp adjustment for live streams - if (playDecision == 0 && streamStartTS > 0) { - //subtract the offset time of when the stream started playing for the client - ts -= streamStartTS; - messageOut.getBody().setTimestamp(ts); - if (log.isTraceEnabled()) { - log.trace("sendMessage (updated): streamStartTS={}, length={}, streamOffset={}, timestamp={}", new Object[] { streamStartTS, currentItem.getLength(), streamOffset, - ts }); - } - } - if (streamStartTS > -1 && currentItem.getLength() >= 0) { - int duration = ts - streamStartTS; - if (duration - streamOffset >= currentItem.getLength()) { - // Sent enough data to client - stop(); - return; - } - } - doPushMessage(messageOut); - } - - /** - * Send clear ping. Lets client know that stream has no more data to - * send. - */ - private void sendClearPing() { - Ping eof = new Ping(); - eof.setEventType(Ping.STREAM_PLAYBUFFER_CLEAR); - eof.setValue2(streamId); - // eos - RTMPMessage eofMsg = RTMPMessage.build(eof); - doPushMessage(eofMsg); - } - - /** - * Send reset message - */ - private void sendReset() { - if (pullMode) { - Ping recorded = new Ping(); - recorded.setEventType(Ping.RECORDED_STREAM); - recorded.setValue2(streamId); - // recorded - RTMPMessage recordedMsg = RTMPMessage.build(recorded); - doPushMessage(recordedMsg); - } - - Ping begin = new Ping(); - begin.setEventType(Ping.STREAM_BEGIN); - begin.setValue2(streamId); - // begin - RTMPMessage beginMsg = RTMPMessage.build(begin); - doPushMessage(beginMsg); - // reset - ResetMessage reset = new ResetMessage(); - doPushMessage(reset); - } - - /** - * Send reset status for item - * @param item Playlist item - */ - private void sendResetStatus(IPlayItem item) { - Status reset = new Status(StatusCodes.NS_PLAY_RESET); - reset.setClientid(streamId); - reset.setDetails(item.getName()); - reset.setDesciption(String.format("Playing and resetting %s.", item.getName())); - - doPushMessage(reset); - } - - /** - * Send playback start status notification - * @param item Playlist item - */ - private void sendStartStatus(IPlayItem item) { - Status start = new Status(StatusCodes.NS_PLAY_START); - start.setClientid(streamId); - start.setDetails(item.getName()); - start.setDesciption(String.format("Started playing %s.", item.getName())); - - doPushMessage(start); - } - - /** - * Send playback stoppage status notification - * @param item Playlist item - */ - private void sendStopStatus(IPlayItem item) { - Status stop = new Status(StatusCodes.NS_PLAY_STOP); - stop.setClientid(streamId); - stop.setDesciption(String.format("Stopped playing %s.", item.getName())); - stop.setDetails(item.getName()); - - doPushMessage(stop); - } - - /** - * Sends an onPlayStatus message. - * - * @param code - * @param duration - * @param bytes - */ - private void sendOnPlayStatus(String code, int duration, long bytes) { - IoBuffer buf = IoBuffer.allocate(255); - buf.setAutoExpand(true); - Output out = new Output(buf); - out.writeString("onPlayStatus"); - Map props = new HashMap(); - props.put("code", code); - props.put("level", "status"); - props.put("duration", duration); - props.put("bytes", bytes); - if (StatusCodes.NS_PLAY_TRANSITION_COMPLETE.equals(code)) { - props.put("details", currentItem.getName()); - props.put("description", String.format("Transitioned to %s", currentItem.getName())); - props.put("clientId", streamId); - props.put("isFastPlay", false); - } - out.writeMap(props); - buf.flip(); - - IRTMPEvent event = new Notify(buf); - if (lastMessageTs > 0) { - event.setTimestamp(lastMessageTs); - } else { - event.setTimestamp(0); - } - RTMPMessage msg = RTMPMessage.build(event); - doPushMessage(msg); - } - - /** - * Send playlist switch status notification - */ - private void sendSwitchStatus() { - // TODO: find correct duration to send - sendOnPlayStatus(StatusCodes.NS_PLAY_SWITCH, 1, bytesSent.get()); - } - - /** - * Send transition status notification - */ - private void sendTransitionStatus() { - sendOnPlayStatus(StatusCodes.NS_PLAY_TRANSITION_COMPLETE, 0, bytesSent.get()); - } - - /** - * Send playlist complete status notification - * - */ - private void sendCompleteStatus() { - // TODO: find correct duration to send - sendOnPlayStatus(StatusCodes.NS_PLAY_COMPLETE, 1, bytesSent.get()); - } - - /** - * Send seek status notification - * @param item Playlist item - * @param position Seek position - */ - private void sendSeekStatus(IPlayItem item, int position) { - Status seek = new Status(StatusCodes.NS_SEEK_NOTIFY); - seek.setClientid(streamId); - seek.setDetails(item.getName()); - seek.setDesciption(String.format("Seeking %d (stream ID: %d).", position, streamId)); - - doPushMessage(seek); - } - - /** - * Send pause status notification - * @param item Playlist item - */ - private void sendPauseStatus(IPlayItem item) { - Status pause = new Status(StatusCodes.NS_PAUSE_NOTIFY); - pause.setClientid(streamId); - pause.setDetails(item.getName()); - - doPushMessage(pause); - } - - /** - * Send resume status notification - * @param item Playlist item - */ - private void sendResumeStatus(IPlayItem item) { - Status resume = new Status(StatusCodes.NS_UNPAUSE_NOTIFY); - resume.setClientid(streamId); - resume.setDetails(item.getName()); - - doPushMessage(resume); - } - - /** - * Send published status notification - * @param item Playlist item - */ - private void sendPublishedStatus(IPlayItem item) { - Status published = new Status(StatusCodes.NS_PLAY_PUBLISHNOTIFY); - published.setClientid(streamId); - published.setDetails(item.getName()); - - doPushMessage(published); - } - - /** - * Send unpublished status notification - * @param item Playlist item - */ - private void sendUnpublishedStatus(IPlayItem item) { - Status unpublished = new Status(StatusCodes.NS_PLAY_UNPUBLISHNOTIFY); - unpublished.setClientid(streamId); - unpublished.setDetails(item.getName()); - - doPushMessage(unpublished); - } - - /** - * Stream not found status notification - * @param item Playlist item - */ - private void sendStreamNotFoundStatus(IPlayItem item) { - Status notFound = new Status(StatusCodes.NS_PLAY_STREAMNOTFOUND); - notFound.setClientid(streamId); - notFound.setLevel(Status.ERROR); - notFound.setDetails(item.getName()); - - doPushMessage(notFound); - } - - /** - * Insufficient bandwidth notification - * @param item Playlist item - */ - private void sendInsufficientBandwidthStatus(IPlayItem item) { - Status insufficientBW = new Status(StatusCodes.NS_PLAY_INSUFFICIENT_BW); - insufficientBW.setClientid(streamId); - insufficientBW.setLevel(Status.WARNING); - insufficientBW.setDetails(item.getName()); - insufficientBW.setDesciption("Data is playing behind the normal speed."); - - doPushMessage(insufficientBW); - } - - /** - * Send VOD init control message - * @param msgIn Message input - * @param item Playlist item - */ - private void sendVODInitCM(IMessageInput msgIn, IPlayItem item) { - OOBControlMessage oobCtrlMsg = new OOBControlMessage(); - oobCtrlMsg.setTarget(IPassive.KEY); - oobCtrlMsg.setServiceName("init"); - Map paramMap = new HashMap(1); - paramMap.put("startTS", (int) item.getStart()); - oobCtrlMsg.setServiceParamMap(paramMap); - msgIn.sendOOBControlMessage(this, oobCtrlMsg); - } - - /** - * Send VOD seek control message - * @param msgIn Message input - * @param position Playlist item - * @return Out-of-band control message call result or -1 on failure - */ - private int sendVODSeekCM(IMessageInput msgIn, int position) { - OOBControlMessage oobCtrlMsg = new OOBControlMessage(); - oobCtrlMsg.setTarget(ISeekableProvider.KEY); - oobCtrlMsg.setServiceName("seek"); - Map paramMap = new HashMap(1); - paramMap.put("position", position); - oobCtrlMsg.setServiceParamMap(paramMap); - msgIn.sendOOBControlMessage(this, oobCtrlMsg); - if (oobCtrlMsg.getResult() instanceof Integer) { - return (Integer) oobCtrlMsg.getResult(); - } else { - return -1; - } - } - - /** - * Send VOD check video control message - * - * @param msgIn - * @return result of oob control message - */ - private boolean sendCheckVideoCM(IMessageInput msgIn) { - OOBControlMessage oobCtrlMsg = new OOBControlMessage(); - oobCtrlMsg.setTarget(IStreamTypeAwareProvider.KEY); - oobCtrlMsg.setServiceName("hasVideo"); - msgIn.sendOOBControlMessage(this, oobCtrlMsg); - if (oobCtrlMsg.getResult() instanceof Boolean) { - return (Boolean) oobCtrlMsg.getResult(); - } else { - return false; - } - } - - /** {@inheritDoc} */ - public void onOOBControlMessage(IMessageComponent source, IPipe pipe, OOBControlMessage oobCtrlMsg) { - if ("ConnectionConsumer".equals(oobCtrlMsg.getTarget())) { - if (source instanceof IProvider) { - if (msgOut != null) { - msgOut.sendOOBControlMessage((IProvider) source, oobCtrlMsg); - } else { - // this may occur when a attempts to play and then disconnects - log.warn("Output is not available, message cannot be sent"); - close(); - } - } - } - } - - /** {@inheritDoc} */ - public void onPipeConnectionEvent(PipeConnectionEvent event) { - switch (event.getType()) { - case PipeConnectionEvent.PROVIDER_CONNECT_PUSH: - if (event.getProvider() != this) { - if (waiting) { - if (waitLiveJob != null) { - schedulingService.removeScheduledJob(waitLiveJob); - } - waitLiveJob = null; - waiting = false; - } - sendPublishedStatus(currentItem); - } - break; - case PipeConnectionEvent.PROVIDER_DISCONNECT: - if (pullMode) { - sendStopStatus(currentItem); - } else { - sendUnpublishedStatus(currentItem); - } - break; - case PipeConnectionEvent.CONSUMER_CONNECT_PULL: - if (event.getConsumer() == this) { - pullMode = true; - } - break; - case PipeConnectionEvent.CONSUMER_CONNECT_PUSH: - if (event.getConsumer() == this) { - pullMode = false; - } - break; - default: - } - } - - /** {@inheritDoc} */ - public void pushMessage(IPipe pipe, IMessage message) throws IOException { - if (message instanceof RTMPMessage) { - RTMPMessage rtmpMessage = (RTMPMessage) message; - IRTMPEvent body = rtmpMessage.getBody(); - if (body instanceof IStreamData) { - // the subscriber paused - if (subscriberStream.getState() == StreamState.PAUSED) { - log.debug("Dropping packet because we are paused"); - videoFrameDropper.dropPacket(rtmpMessage); - return; - } - if (body instanceof VideoData) { - if (msgIn instanceof IBroadcastScope) { - IBroadcastStream stream = (IBroadcastStream) ((IBroadcastScope) msgIn).getClientBroadcastStream(); - if (stream != null && stream.getCodecInfo() != null) { - IVideoStreamCodec videoCodec = stream.getCodecInfo().getVideoCodec(); - //dont try to drop frames if video codec is null - related to SN-77 - if (videoCodec != null && videoCodec.canDropFrames()) { - if (!receiveVideo) { - // The client disabled video or the app doesn't have enough bandwidth - // allowed for this stream. - log.debug("Dropping packet because we cant receive video or token acquire failed"); - videoFrameDropper.dropPacket(rtmpMessage); - return; - } - // Only check for frame dropping if the codec supports it - long pendingVideos = pendingVideoMessages(); - if (!videoFrameDropper.canSendPacket(rtmpMessage, pendingVideos)) { - // Drop frame as it depends on other frames that were dropped before. - log.debug("Dropping packet because frame dropper says we cant send it"); - return; - } - // increment the number of times we had pending video frames sequentially - if (pendingVideos > 1) { - numSequentialPendingVideoFrames++; - } else { - // reset number of sequential pending frames if 1 or 0 are pending. - numSequentialPendingVideoFrames = 0; - } - if (pendingVideos > maxPendingVideoFramesThreshold || numSequentialPendingVideoFrames > maxSequentialPendingVideoFrames) { - log.debug("Pending: {} Threshold: {} Sequential: {}", new Object[] { pendingVideos, maxPendingVideoFramesThreshold, - numSequentialPendingVideoFrames }); - // We drop because the client has insufficient bandwidth. - long now = System.currentTimeMillis(); - if (bufferCheckInterval > 0 && now >= nextCheckBufferUnderrun) { - // Notify client about frame dropping (keyframe) - sendInsufficientBandwidthStatus(currentItem); - nextCheckBufferUnderrun = now + bufferCheckInterval; - } - videoFrameDropper.dropPacket(rtmpMessage); - return; - } - } - } - } - } else if (body instanceof AudioData) { - if (!receiveAudio && sendBlankAudio) { - // Send blank audio packet to reset player - sendBlankAudio = false; - body = new AudioData(); - if (lastMessageTs > 0) { - body.setTimestamp(lastMessageTs); - } else { - body.setTimestamp(0); - } - rtmpMessage = RTMPMessage.build(body); - } else if (!receiveAudio) { - return; - } - } - sendMessage(rtmpMessage); - } else { - throw new RuntimeException(String.format("Expected IStreamData but got %s (type %s)", body.getClass(), body.getDataType())); - } - } else if (message instanceof ResetMessage) { - sendReset(); - } else { - msgOut.pushMessage(message); - } - } - - /** - * Get number of pending video messages - * @return Number of pending video messages - */ - private long pendingVideoMessages() { - if (msgOut != null) { - OOBControlMessage pendingRequest = new OOBControlMessage(); - pendingRequest.setTarget("ConnectionConsumer"); - pendingRequest.setServiceName("pendingVideoCount"); - msgOut.sendOOBControlMessage(this, pendingRequest); - if (pendingRequest.getResult() != null) { - return (Long) pendingRequest.getResult(); - } else { - return 0; - } - } - return 0; - } - - /** - * Get number of pending messages to be sent - * @return Number of pending messages - */ - private long pendingMessages() { - return subscriberStream.getConnection().getPendingMessages(); - } - - public boolean isPullMode() { - return pullMode; - } - - public boolean isPaused() { - return subscriberStream.isPaused(); - } - - /** - * Returns the timestamp of the last message sent. - * - * @return last message timestamp - */ - public int getLastMessageTimestamp() { - return lastMessageTs; - } - - public long getPlaybackStart() { - return playbackStart; - } - - public void sendBlankAudio(boolean sendBlankAudio) { - this.sendBlankAudio = sendBlankAudio; - } - - /** - * Returns true if the engine currently receives audio. - * - * @return receive audio - */ - public boolean receiveAudio() { - return receiveAudio; - } - - /** - * Returns true if the engine currently receives audio and - * sets the new value. - * - * @param receive new value - * @return old value - */ - public boolean receiveAudio(boolean receive) { - boolean oldValue = receiveAudio; - //set new value - if (receiveAudio != receive) { - receiveAudio = receive; - } - return oldValue; - } - - /** - * Returns true if the engine currently receives video. - * - * @return receive video - */ - public boolean receiveVideo() { - return receiveVideo; - } - - /** - * Returns true if the engine currently receives video and - * sets the new value. - * @param receive new value - * @return old value - */ - public boolean receiveVideo(boolean receive) { - boolean oldValue = receiveVideo; - //set new value - if (receiveVideo != receive) { - receiveVideo = receive; - } - return oldValue; - } - - /** - * Releases pending message body, nullifies pending message object - */ - private void releasePendingMessage() { - if (pendingMessage != null) { - IRTMPEvent body = pendingMessage.getBody(); - if (body instanceof IStreamData && ((IStreamData) body).getData() != null) { - ((IStreamData) body).getData().free(); - } - pendingMessage = null; - } - } - - /** - * Check if sending the given message was enabled by the client. - * - * @param message the message to check - * @return true if the message should be sent, false otherwise (and the message is discarded) - */ - protected boolean checkSendMessageEnabled(RTMPMessage message) { - IRTMPEvent body = message.getBody(); - if (!receiveAudio && body instanceof AudioData) { - // The user doesn't want to get audio packets - ((IStreamData) body).getData().free(); - if (sendBlankAudio) { - // Send reset audio packet - sendBlankAudio = false; - body = new AudioData(); - // We need a zero timestamp - if (lastMessageTs >= 0) { - body.setTimestamp(lastMessageTs - timestampOffset); - } else { - body.setTimestamp(-timestampOffset); - } - message = RTMPMessage.build(body); - } else { - return false; - } - } else if (!receiveVideo && body instanceof VideoData) { - // The user doesn't want to get video packets - ((IStreamData) body).getData().free(); - return false; - } - return true; - } - - /** - * Schedule a stop to be run from a separate thread to allow the background thread to stop cleanly. - */ - private void runDeferredStop() { - // Stop current jobs from running. - clearWaitJobs(); - // Schedule deferred stop executor. - log.trace("Ran deferred stop"); - if (deferredStop == null) { - // set deferred stop if we get a job name returned - deferredStop = subscriberStream.scheduleWithFixedDelay(new DeferredStopRunnable(), 100); - } - } - - private void cancelDeferredStop() { - log.debug("Cancel deferred stop"); - if (deferredStop != null) { - subscriberStream.cancelJob(deferredStop); - deferredStop = null; - } - ensurePullAndPushRunning(); - } - - /** - * Runnable worker to handle seek operations. - */ - private final class SeekRunnable implements Runnable { - - private final int position; - - SeekRunnable(int position) { - this.position = position; - } - - @SuppressWarnings("incomplete-switch") - public void run() { - log.trace("Seek: {}", position); - boolean startPullPushThread = false; - switch (subscriberStream.getState()) { - case PLAYING: - startPullPushThread = true; - case PAUSED: - case STOPPED: - //allow seek if playing, paused, or stopped - if (!pullMode) { - // throw new OperationNotSupportedException(); - throw new RuntimeException(); - } - releasePendingMessage(); - clearWaitJobs(); - break; - default: - throw new IllegalStateException("Cannot seek in current state"); - } - sendClearPing(); - sendReset(); - sendSeekStatus(currentItem, position); - sendStartStatus(currentItem); - int seekPos = sendVODSeekCM(msgIn, position); - // we seeked to the nearest keyframe so use real timestamp now - if (seekPos == -1) { - seekPos = position; - } - //what should our start be? - log.trace("Current playback start: {}", playbackStart); - playbackStart = System.currentTimeMillis() - seekPos; - log.trace("Playback start: {} seek pos: {}", playbackStart, seekPos); - subscriberStream.onChange(StreamState.SEEK, currentItem, seekPos); - // start off with not having sent any message - boolean messageSent = false; - // read our client state - switch (subscriberStream.getState()) { - case PAUSED: - case STOPPED: - // we send a single snapshot on pause - if (sendCheckVideoCM(msgIn)) { - IMessage msg = null; - do { - try { - msg = msgIn.pullMessage(); - } catch (Throwable err) { - log.error("Error while pulling message", err); - msg = null; - } - if (msg instanceof RTMPMessage) { - RTMPMessage rtmpMessage = (RTMPMessage) msg; - IRTMPEvent body = rtmpMessage.getBody(); - if (body instanceof VideoData && ((VideoData) body).getFrameType() == FrameType.KEYFRAME) { - //body.setTimestamp(seekPos); - doPushMessage(rtmpMessage); - rtmpMessage.getBody().release(); - messageSent = true; - lastMessageTs = body.getTimestamp(); - break; - } - } - } while (msg != null); - } - } - // seeked past end of stream - if (currentItem.getLength() >= 0 && (position - streamOffset) >= currentItem.getLength()) { - stop(); - } - // if no message has been sent by this point send an audio packet - if (!messageSent) { - // Send blank audio packet to notify client about new position - log.debug("Sending blank audio packet"); - AudioData audio = new AudioData(); - audio.setTimestamp(seekPos); - audio.setHeader(new Header()); - audio.getHeader().setTimer(seekPos); - RTMPMessage audioMessage = RTMPMessage.build(audio); - lastMessageTs = seekPos; - doPushMessage(audioMessage); - audioMessage.getBody().release(); - } - - if (!messageSent && subscriberStream.getState() == StreamState.PLAYING) { - boolean isRTMPTPlayback = subscriberStream.getConnection() instanceof RTMPTConnection; - - // send all frames from last keyframe up to requested position and fill client buffer - if (sendCheckVideoCM(msgIn)) { - final long clientBuffer = subscriberStream.getClientBufferDuration(); - IMessage msg = null; - int msgSent = 0; - - do { - try { - msg = msgIn != null ? msgIn.pullMessage() : null; - if (msg instanceof RTMPMessage) { - RTMPMessage rtmpMessage = (RTMPMessage) msg; - IRTMPEvent body = rtmpMessage.getBody(); - if (body.getTimestamp() >= position + (clientBuffer * 2)) { - // client buffer should be full by now, continue regular pull/push - releasePendingMessage(); - if (checkSendMessageEnabled(rtmpMessage)) { - pendingMessage = rtmpMessage; - } - break; - } - if (!checkSendMessageEnabled(rtmpMessage)) { - continue; - } - msgSent++; - sendMessage(rtmpMessage); - } - } catch (Throwable err) { - log.error("Error while pulling message", err); - msg = null; - } - } while (!isRTMPTPlayback && (msg != null)); - - log.trace("msgSent: {}", msgSent); - playbackStart = System.currentTimeMillis() - lastMessageTs; - } - } - // start pull-push - if (startPullPushThread) { - ensurePullAndPushRunning(); - } - } - } - - /** - * Periodically triggered by executor to send messages to the client. - */ - private final class PullAndPushRunnable implements IScheduledJob { - - /** - * Trigger sending of messages. - */ - public void execute(ISchedulingService svc) { - // ensure the job is not already running - if (pushPullRunning.compareAndSet(false, true)) { - try { - // handle any pending operations - Runnable worker = null; - while (!pendingOperations.isEmpty()) { - log.debug("Pending operations: {}", pendingOperations.size()); - // remove the first operation and execute it - worker = pendingOperations.remove(); - log.debug("Worker: {}", worker); - // if the operation is seek, ensure it is the last request in the set - while (worker instanceof SeekRunnable) { - Runnable tmp = pendingOperations.peek(); - if (tmp != null && tmp instanceof SeekRunnable) { - worker = pendingOperations.remove(); - } else { - break; - } - } - if (worker != null) { - log.debug("Executing pending operation"); - worker.run(); - } - } - // receive then send if message is data (not audio or video) - if (subscriberStream.getState() == StreamState.PLAYING && pullMode) { - if (pendingMessage != null) { - IRTMPEvent body = pendingMessage.getBody(); - if (okayToSendMessage(body)) { - sendMessage(pendingMessage); - releasePendingMessage(); - } else { - return; - } - } else { - IMessage msg = null; - do { - msg = msgIn.pullMessage(); - if (msg != null) { - if (msg instanceof RTMPMessage) { - RTMPMessage rtmpMessage = (RTMPMessage) msg; - if (checkSendMessageEnabled(rtmpMessage)) { - // Adjust timestamp when playing lists - IRTMPEvent body = rtmpMessage.getBody(); - body.setTimestamp(body.getTimestamp() + timestampOffset); - if (okayToSendMessage(body)) { - log.trace("ts: {}", rtmpMessage.getBody().getTimestamp()); - sendMessage(rtmpMessage); - ((IStreamData) body).getData().free(); - } else { - pendingMessage = rtmpMessage; - } - ensurePullAndPushRunning(); - break; - } - } - } else { - // No more packets to send - log.debug("Ran out of packets"); - runDeferredStop(); - } - } while (msg != null); - } - } - } catch (IOException err) { - // we couldn't get more data, stop stream. - log.error("Error while getting message", err); - runDeferredStop(); - } finally { - // reset running flag - pushPullRunning.compareAndSet(true, false); - } - } else { - log.debug("Push / pull already running"); - } - } - } - - private class DeferredStopRunnable implements IScheduledJob { - - public void execute(ISchedulingService service) { - if (isClientBufferEmpty()) { - log.trace("Buffer is empty, stop will proceed"); - stop(); - } - } - - } - - /** - * @param maxPendingVideoFrames the maxPendingVideoFrames to set - */ - public void setMaxPendingVideoFrames(int maxPendingVideoFrames) { - this.maxPendingVideoFramesThreshold = maxPendingVideoFrames; - } - - /** - * @param maxSequentialPendingVideoFrames the maxSequentialPendingVideoFrames to set - */ - public void setMaxSequentialPendingVideoFrames(int maxSequentialPendingVideoFrames) { - this.maxSequentialPendingVideoFrames = maxSequentialPendingVideoFrames; - } -} diff --git a/src/main/java/org/red5/server/stream/PlaylistSubscriberStream.java b/src/main/java/org/red5/server/stream/PlaylistSubscriberStream.java deleted file mode 100644 index 181c1072c..000000000 --- a/src/main/java/org/red5/server/stream/PlaylistSubscriberStream.java +++ /dev/null @@ -1,865 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.io.IOException; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Set; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import org.red5.logging.Red5LoggerFactory; -import org.red5.server.api.IConnection; -import org.red5.server.api.IContext; -import org.red5.server.api.Red5; -import org.red5.server.api.scheduling.IScheduledJob; -import org.red5.server.api.scheduling.ISchedulingService; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.statistics.IPlaylistSubscriberStreamStatistics; -import org.red5.server.api.stream.IPlayItem; -import org.red5.server.api.stream.IPlaylistController; -import org.red5.server.api.stream.IPlaylistSubscriberStream; -import org.red5.server.api.stream.IStreamAwareScopeHandler; -import org.red5.server.api.stream.OperationNotSupportedException; -import org.red5.server.api.stream.StreamState; -import org.slf4j.Logger; - -/** - * Stream of playlist subscriber - */ -public class PlaylistSubscriberStream extends AbstractClientStream implements IPlaylistSubscriberStream, IPlaylistSubscriberStreamStatistics { - - private static final Logger log = Red5LoggerFactory.getLogger(PlaylistSubscriberStream.class); - - private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - - private final Lock read = readWriteLock.readLock(); - - private final Lock write = readWriteLock.writeLock(); - - /** - * Playlist controller - */ - private IPlaylistController controller; - - /** - * Default playlist controller - */ - private IPlaylistController defaultController; - - /** - * Playlist items - */ - private final LinkedList items; - - /** - * Current item index - */ - private int currentItemIndex = 0; - - /** - * Plays items back - */ - protected PlayEngine engine; - - /** - * Rewind mode state - */ - protected boolean rewind; - - /** - * Random mode state - */ - protected boolean random; - - /** - * Repeat mode state - */ - protected boolean repeat; - - /** - * Service used to provide notifications, keep client buffer filled, clean up, etc... - */ - protected ISchedulingService schedulingService; - - /** - * Scheduled job names - */ - protected Set jobs = new HashSet(1); - - /** - * Interval in ms to check for buffer underruns in VOD streams. - */ - protected int bufferCheckInterval = 0; - - /** - * Number of pending messages at which a NetStream.Play.InsufficientBW - * message is generated for VOD streams. - */ - protected int underrunTrigger = 10; - - /** - * Timestamp this stream was created. - */ - protected long creationTime = System.currentTimeMillis(); - - /** - * Number of bytes sent. - */ - protected long bytesSent = 0; - - /** Constructs a new PlaylistSubscriberStream. */ - public PlaylistSubscriberStream() { - defaultController = new SimplePlaylistController(); - items = new LinkedList(); - } - - /** - * Creates a play engine based on current services (scheduling service, consumer service, and provider service). - * This method is useful during unit testing. - */ - PlayEngine createEngine(ISchedulingService schedulingService, IConsumerService consumerService, IProviderService providerService) { - engine = new PlayEngine.Builder(this, schedulingService, consumerService, providerService).build(); - return engine; - } - - /** - * Set interval to check for buffer underruns. Set to 0 to - * disable. - * - * @param bufferCheckInterval interval in ms - */ - public void setBufferCheckInterval(int bufferCheckInterval) { - this.bufferCheckInterval = bufferCheckInterval; - } - - /** - * Set maximum number of pending messages at which a - * NetStream.Play.InsufficientBW message will be - * generated for VOD streams - * - * @param underrunTrigger the maximum number of pending messages - */ - public void setUnderrunTrigger(int underrunTrigger) { - this.underrunTrigger = underrunTrigger; - } - - /** {@inheritDoc} */ - public void start() { - //ensure the play engine exists - if (engine == null) { - IScope scope = getScope(); - if (scope != null) { - IContext ctx = scope.getContext(); - if (ctx.hasBean(ISchedulingService.BEAN_NAME)) { - schedulingService = (ISchedulingService) ctx.getBean(ISchedulingService.BEAN_NAME); - } else { - //try the parent - schedulingService = (ISchedulingService) scope.getParent().getContext().getBean(ISchedulingService.BEAN_NAME); - } - IConsumerService consumerService = null; - if (ctx.hasBean(IConsumerService.KEY)) { - consumerService = (IConsumerService) ctx.getBean(IConsumerService.KEY); - } else { - //try the parent - consumerService = (IConsumerService) scope.getParent().getContext().getBean(IConsumerService.KEY); - } - IProviderService providerService = null; - if (ctx.hasBean(IProviderService.BEAN_NAME)) { - providerService = (IProviderService) ctx.getBean(IProviderService.BEAN_NAME); - } else { - //try the parent - providerService = (IProviderService) scope.getParent().getContext().getBean(IProviderService.BEAN_NAME); - } - - engine = new PlayEngine.Builder(this, schedulingService, consumerService, providerService).build(); - } else { - log.info("Scope was null on start"); - } - } - //set buffer check interval - engine.setBufferCheckInterval(bufferCheckInterval); - //set underrun trigger - engine.setUnderrunTrigger(underrunTrigger); - // Start playback engine - engine.start(); - // Notify subscribers on start - onChange(StreamState.STARTED); - } - - /** {@inheritDoc} */ - public void play() throws IOException { - // Check how many is yet to play... - int count = items.size(); - // Return if playlist is empty - if (count == 0) { - return; - } - // Move to next if current item is set to -1 - if (currentItemIndex == -1) { - moveToNext(); - } - // If there's some more items on list then play current item - while (count-- > 0) { - IPlayItem item = null; - read.lock(); - try { - // Get playlist item - item = items.get(currentItemIndex); - engine.play(item); - break; - } catch (StreamNotFoundException e) { - // go for next item - moveToNext(); - if (currentItemIndex == -1) { - // we reaches the end. - break; - } - item = items.get(currentItemIndex); - } catch (IllegalStateException e) { - // an stream is already playing - break; - } finally { - read.unlock(); - } - } - } - - /** {@inheritDoc} */ - public void pause(int position) { - try { - engine.pause(position); - } catch (IllegalStateException e) { - log.debug("pause caught an IllegalStateException"); - } - } - - /** {@inheritDoc} */ - public void resume(int position) { - try { - engine.resume(position); - } catch (IllegalStateException e) { - log.debug("resume caught an IllegalStateException"); - } - } - - /** {@inheritDoc} */ - public void stop() { - try { - engine.stop(); - } catch (IllegalStateException e) { - log.debug("stop caught an IllegalStateException"); - } - } - - /** {@inheritDoc} */ - public void seek(int position) throws OperationNotSupportedException { - try { - engine.seek(position); - } catch (IllegalStateException e) { - log.debug("seek caught an IllegalStateException"); - } - } - - /** {@inheritDoc} */ - public void close() { - if (engine != null) { - engine.close(); - onChange(StreamState.CLOSED); - items.clear(); - // clear jobs - if (schedulingService != null && !jobs.isEmpty()) { - for (String jobName : jobs) { - schedulingService.removeScheduledJob(jobName); - } - jobs.clear(); - } - } - } - - /** {@inheritDoc} */ - public boolean isPaused() { - return state == StreamState.PAUSED; - } - - /** {@inheritDoc} */ - public void addItem(IPlayItem item) { - write.lock(); - try { - items.add(item); - } finally { - write.unlock(); - } - } - - /** {@inheritDoc} */ - public void addItem(IPlayItem item, int index) { - write.lock(); - try { - items.add(index, item); - } finally { - write.unlock(); - } - } - - /** {@inheritDoc} */ - public void removeItem(int index) { - if (index < 0 || index >= items.size()) { - return; - } - int originSize = items.size(); - write.lock(); - try { - items.remove(index); - } finally { - write.unlock(); - } - if (currentItemIndex == index) { - // set the next item. - if (index == originSize - 1) { - currentItemIndex = index - 1; - } - } - } - - /** {@inheritDoc} */ - public void removeAllItems() { - // we try to stop the engine first - stop(); - write.lock(); - try { - items.clear(); - } finally { - write.unlock(); - } - } - - /** {@inheritDoc} */ - public void previousItem() { - stop(); - moveToPrevious(); - if (currentItemIndex == -1) { - return; - } - IPlayItem item = null; - int count = items.size(); - while (count-- > 0) { - read.lock(); - try { - item = items.get(currentItemIndex); - engine.play(item); - break; - } catch (IOException err) { - log.error("Error while starting to play item, moving to previous.", err); - // go for next item - moveToPrevious(); - if (currentItemIndex == -1) { - // we reaches the end. - break; - } - } catch (StreamNotFoundException e) { - // go for next item - moveToPrevious(); - if (currentItemIndex == -1) { - // we reaches the end. - break; - } - } catch (IllegalStateException e) { - // an stream is already playing - break; - } finally { - read.unlock(); - } - } - } - - /** {@inheritDoc} */ - public boolean hasMoreItems() { - int nextItem = currentItemIndex + 1; - if (nextItem >= items.size() && !repeat) { - return false; - } else { - return true; - } - } - - /** {@inheritDoc} */ - public void nextItem() { - moveToNext(); - if (currentItemIndex == -1) { - return; - } - IPlayItem item = null; - int count = items.size(); - while (count-- > 0) { - read.lock(); - try { - item = items.get(currentItemIndex); - engine.play(item, false); - break; - } catch (IOException err) { - log.error("Error while starting to play item, moving to next", err); - // go for next item - moveToNext(); - if (currentItemIndex == -1) { - // we reaches the end. - break; - } - } catch (StreamNotFoundException e) { - // go for next item - moveToNext(); - if (currentItemIndex == -1) { - // we reaches the end. - break; - } - } catch (IllegalStateException e) { - // an stream is already playing - break; - } finally { - read.unlock(); - } - } - } - - /** {@inheritDoc} */ - public void setItem(int index) { - if (index < 0 || index >= items.size()) { - return; - } - stop(); - currentItemIndex = index; - read.lock(); - try { - IPlayItem item = items.get(currentItemIndex); - engine.play(item); - } catch (IOException e) { - log.error("setItem caught a IOException", e); - } catch (StreamNotFoundException e) { - // let the engine retain the STOPPED state - // and wait for control from outside - log.debug("setItem caught a StreamNotFoundException"); - } catch (IllegalStateException e) { - log.error("Illegal state exception on playlist item setup", e); - } finally { - read.unlock(); - } - } - - /** {@inheritDoc} */ - public boolean isRandom() { - return random; - } - - /** {@inheritDoc} */ - public void setRandom(boolean random) { - this.random = random; - } - - /** {@inheritDoc} */ - public boolean isRewind() { - return rewind; - } - - /** {@inheritDoc} */ - public void setRewind(boolean rewind) { - this.rewind = rewind; - } - - /** {@inheritDoc} */ - public boolean isRepeat() { - return repeat; - } - - /** {@inheritDoc} */ - public void setRepeat(boolean repeat) { - this.repeat = repeat; - } - - /** - * Seek to current position to restart playback with audio and/or video. - */ - private void seekToCurrentPlayback() { - if (engine.isPullMode()) { - try { - // TODO: figure out if this is the correct position to seek to - final long delta = System.currentTimeMillis() - engine.getPlaybackStart(); - engine.seek((int) delta); - } catch (OperationNotSupportedException err) { - // Ignore error, should not happen for pullMode engines - } - } - } - - /** {@inheritDoc} */ - public void receiveVideo(boolean receive) { - boolean receiveVideo = engine.receiveVideo(receive); - if (!receiveVideo && receive) { - //video has been re-enabled - seekToCurrentPlayback(); - } - } - - /** {@inheritDoc} */ - public void receiveAudio(boolean receive) { - //check if engine currently receives audio, returns previous value - boolean receiveAudio = engine.receiveAudio(receive); - if (receiveAudio && !receive) { - //send a blank audio packet to reset the player - engine.sendBlankAudio(true); - } else if (!receiveAudio && receive) { - //do a seek - seekToCurrentPlayback(); - } - } - - /** {@inheritDoc} */ - public void setPlaylistController(IPlaylistController controller) { - this.controller = controller; - } - - /** {@inheritDoc} */ - public int getItemSize() { - return items.size(); - } - - /** {@inheritDoc} */ - public int getCurrentItemIndex() { - return currentItemIndex; - } - - /** - * {@inheritDoc} - */ - public IPlayItem getCurrentItem() { - return getItem(getCurrentItemIndex()); - } - - /** {@inheritDoc} */ - public IPlayItem getItem(int index) { - read.lock(); - try { - return items.get(index); - } catch (IndexOutOfBoundsException e) { - return null; - } finally { - read.unlock(); - } - } - - /** {@inheritDoc} */ - public boolean replace(IPlayItem oldItem, IPlayItem newItem) { - boolean result = false; - read.lock(); - try { - int index = items.indexOf(oldItem); - items.remove(index); - items.set(index, newItem); - result = true; - } catch (Exception e) { - - } finally { - read.unlock(); - } - return result; - } - - /** - * Move the current item to the next in list. - */ - private void moveToNext() { - if (controller != null) { - currentItemIndex = controller.nextItem(this, currentItemIndex); - } else { - currentItemIndex = defaultController.nextItem(this, currentItemIndex); - } - } - - /** - * Move the current item to the previous in list. - */ - private void moveToPrevious() { - if (controller != null) { - currentItemIndex = controller.previousItem(this, currentItemIndex); - } else { - currentItemIndex = defaultController.previousItem(this, currentItemIndex); - } - } - - /** - * {@inheritDoc} - */ - public void onChange(final StreamState state, final Object... changed) { - Notifier notifier = null; - IStreamAwareScopeHandler handler = getStreamAwareHandler(); - switch (state) { - case SEEK: - //notifies subscribers on seek - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get item being played - IPlayItem item = (IPlayItem) changed[0]; - //seek position - int position = (Integer) changed[1]; - try { - handler.streamPlayItemSeek(stream, item, position); - } catch (Throwable t) { - log.error("error notify streamPlayItemSeek", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case PAUSED: - //set the paused state - this.setState(StreamState.PAUSED); - //notifies subscribers on pause - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get item being played - IPlayItem item = (IPlayItem) changed[0]; - //playback position - int position = (Integer) changed[1]; - try { - handler.streamPlayItemPause(stream, item, position); - } catch (Throwable t) { - log.error("error notify streamPlayItemPause", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case RESUMED: - //resume playing - this.setState(StreamState.PLAYING); - //notifies subscribers on resume - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get item being played - IPlayItem item = (IPlayItem) changed[0]; - //playback position - int position = (Integer) changed[1]; - try { - handler.streamPlayItemResume(stream, item, position); - } catch (Throwable t) { - log.error("error notify streamPlayItemResume", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case PLAYING: - //notifies subscribers on play - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get item being played - IPlayItem item = (IPlayItem) changed[0]; - //is it a live broadcast - boolean isLive = (Boolean) changed[1]; - try { - handler.streamPlayItemPlay(stream, item, isLive); - } catch (Throwable t) { - log.error("error notify streamPlayItemPlay", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case CLOSED: - //notifies subscribers on close - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - try { - handler.streamSubscriberClose(stream); - } catch (Throwable t) { - log.error("error notify streamSubscriberClose", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case STARTED: - //notifies subscribers on start - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - try { - handler.streamSubscriberStart(stream); - } catch (Throwable t) { - log.error("error notify streamSubscriberStart", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case STOPPED: - //set the stopped state - this.setState(StreamState.STOPPED); - //notifies subscribers on stop - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get the item that was stopped - IPlayItem item = (IPlayItem) changed[0]; - try { - handler.streamPlayItemStop(stream, item); - } catch (Throwable t) { - log.error("error notify streamPlaylistItemStop", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case END: - //notified by the play engine when the current item reaches the end - nextItem(); - break; - default: - //there is no "default" handling - log.warn("Unhandled change: {}", state); - } - if (notifier != null) { - IConnection conn = Red5.getConnectionLocal(); - notifier.setConnection(conn); - scheduleOnceJob(notifier); - } - } - - /** {@inheritDoc} */ - public IPlaylistSubscriberStreamStatistics getStatistics() { - return this; - } - - /** {@inheritDoc} */ - public long getCreationTime() { - return creationTime; - } - - /** {@inheritDoc} */ - public int getCurrentTimestamp() { - int lastMessageTs = engine.getLastMessageTimestamp(); - if (lastMessageTs >= 0) { - return 0; - } - return lastMessageTs; - } - - /** {@inheritDoc} */ - public long getBytesSent() { - return bytesSent; - } - - /** {@inheritDoc} */ - public double getEstimatedBufferFill() { - // check to see if any messages have been sent - int lastMessageTs = engine.getLastMessageTimestamp(); - if (lastMessageTs < 0) { - // nothing has been sent yet - return 0.0; - } - // buffer size as requested by the client - final long buffer = getClientBufferDuration(); - if (buffer == 0) { - return 100.0; - } - // duration the stream is playing - final long delta = System.currentTimeMillis() - engine.getPlaybackStart(); - // expected amount of data present in client buffer - final long buffered = lastMessageTs - delta; - return (buffered * 100.0) / buffer; - } - - /** {@inheritDoc} */ - public String scheduleOnceJob(IScheduledJob job) { - String jobName = schedulingService.addScheduledOnceJob(10, job); - return jobName; - } - - /** {@inheritDoc} */ - public String scheduleWithFixedDelay(IScheduledJob job, int interval) { - String jobName = schedulingService.addScheduledJob(interval, job); - jobs.add(jobName); - return jobName; - } - - /** {@inheritDoc} */ - public void cancelJob(String jobName) { - schedulingService.removeScheduledJob(jobName); - } - - /** - * Handles notifications in a separate thread. - */ - public class Notifier implements IScheduledJob { - - IPlaylistSubscriberStream stream; - - IStreamAwareScopeHandler handler; - - IConnection conn; - - public Notifier(IPlaylistSubscriberStream stream, IStreamAwareScopeHandler handler) { - log.trace("Notifier - stream: {} handler: {}", stream, handler); - this.stream = stream; - this.handler = handler; - } - - public void setConnection(IConnection conn) { - this.conn = conn; - } - - public void execute(ISchedulingService service) { - } - - } - -} diff --git a/src/main/java/org/red5/server/stream/RecordingListener.java b/src/main/java/org/red5/server/stream/RecordingListener.java deleted file mode 100644 index c69435cb0..000000000 --- a/src/main/java/org/red5/server/stream/RecordingListener.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.io.File; -import java.io.IOException; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.io.IKeyFrameMetaCache; -import org.red5.server.api.IConnection; -import org.red5.server.api.scheduling.IScheduledJob; -import org.red5.server.api.scheduling.ISchedulingService; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.stream.IBroadcastStream; -import org.red5.server.api.stream.IStreamFilenameGenerator; -import org.red5.server.api.stream.IStreamFilenameGenerator.GenerationType; -import org.red5.server.api.stream.IStreamPacket; -import org.red5.server.net.rtmp.event.Aggregate; -import org.red5.server.net.rtmp.event.AudioData; -import org.red5.server.net.rtmp.event.CachedEvent; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.scheduling.QuartzSchedulingService; -import org.red5.server.stream.consumer.FileConsumer; -import org.red5.server.stream.message.RTMPMessage; -import org.red5.server.util.ScopeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; - -/** - * Stream listener for recording stream events to a file. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class RecordingListener implements IRecordingListener { - - private static final Logger log = LoggerFactory.getLogger(RecordingListener.class); - - /** - * Scheduler - */ - private QuartzSchedulingService scheduler; - - /** - * Event queue worker job name - */ - private String eventQueueJobName; - - /** - * Whether we are recording or not - */ - private AtomicBoolean recording = new AtomicBoolean(false); - - /** - * Whether we are appending or not - */ - private boolean appending; - - /** - * FileConsumer used to output recording to disk - */ - private FileConsumer recordingConsumer; - - /** - * The filename we are recording to. - */ - private String fileName; - - /** - * Queue to hold incoming stream event packets. - */ - private final BlockingQueue queue = new LinkedBlockingQueue(); - - /** - * Get the file we'd be recording to based on scope and given name. - * - * @param scope - * @param name - * @return file - */ - public static File getRecordFile(IScope scope, String name) { - // get stream filename generator - IStreamFilenameGenerator generator = (IStreamFilenameGenerator) ScopeUtils.getScopeService(scope, IStreamFilenameGenerator.class, DefaultStreamFilenameGenerator.class); - // generate filename - String fileName = generator.generateFilename(scope, name, ".flv", GenerationType.RECORD); - File file = null; - if (generator.resolvesToAbsolutePath()) { - file = new File(fileName); - } else { - Resource resource = scope.getContext().getResource(fileName); - if (resource.exists()) { - try { - file = resource.getFile(); - log.debug("File exists: {} writable: {}", file.exists(), file.canWrite()); - } catch (IOException ioe) { - log.error("File error: {}", ioe); - } - } else { - String appScopeName = ScopeUtils.findApplication(scope).getName(); - file = new File(String.format("%s/webapps/%s/%s", System.getProperty("red5.root"), appScopeName, fileName)); - } - } - return file; - } - - /** {@inheritDoc} */ - public boolean init(IConnection conn, String name, boolean isAppend) { - // get connections scope - return init(conn.getScope(), name, isAppend); - } - - /** {@inheritDoc} */ - public boolean init(IScope scope, String name, boolean isAppend) { - // get the file for our filename - File file = getRecordFile(scope, name); - if (file != null) { - // If append mode is on... - if (!isAppend) { - if (file.exists()) { - // when "live" or "record" is used, any previously recorded stream with the same stream URI is deleted. - if (!file.delete()) { - log.warn("Existing file: {} could not be deleted", file.getName()); - return false; - } - } - } else { - if (file.exists()) { - appending = true; - } else { - // if a recorded stream at the same URI does not already exist, "append" creates the stream as though "record" was passed. - isAppend = false; - } - } - // if the file doesn't exist yet, create it - if (!file.exists()) { - // Make sure the destination directory exists - String path = file.getAbsolutePath(); - int slashPos = path.lastIndexOf(File.separator); - if (slashPos != -1) { - path = path.substring(0, slashPos); - } - File tmp = new File(path); - if (!tmp.isDirectory()) { - tmp.mkdirs(); - } - try { - file.createNewFile(); - } catch (IOException e) { - log.warn("New recording file could not be created for: {}", file.getName(), e); - return false; - } - } - if (log.isDebugEnabled()) { - try { - log.debug("Recording file: {}", file.getCanonicalPath()); - } catch (IOException e) { - log.warn("Exception getting file path", e); - } - } - //remove existing meta info - if (scope.getContext().hasBean("keyframe.cache")) { - IKeyFrameMetaCache keyFrameCache = (IKeyFrameMetaCache) scope.getContext().getBean("keyframe.cache"); - keyFrameCache.removeKeyFrameMeta(file); - } - // get instance via spring - if (scope.getContext().hasBean("fileConsumer")) { - log.debug("Context contains a file consumer"); - recordingConsumer = (FileConsumer) scope.getContext().getBean("fileConsumer"); - recordingConsumer.setScope(scope); - recordingConsumer.setFile(file); - } else { - log.debug("Context does not contain a file consumer, using direct instance"); - // get a new instance - recordingConsumer = new FileConsumer(scope, file); - } - // set the mode on the consumer - if (isAppend) { - recordingConsumer.setMode("append"); - } else { - recordingConsumer.setMode("record"); - } - // set the filename - setFileName(file.getName()); - // get the scheduler - scheduler = (QuartzSchedulingService) scope.getParent().getContext().getBean(QuartzSchedulingService.BEAN_NAME); - // set recording true - recording.set(true); - } else { - log.warn("Record file is null"); - } - // since init finished, return recording flag - return recording.get(); - } - - /** {@inheritDoc} */ - public void start() { - // start the worker - eventQueueJobName = scheduler.addScheduledJob(3000, new EventQueueJob()); - } - - /** {@inheritDoc} */ - public void stop() { - // set the record flag to false - if (recording.compareAndSet(true, false)) { - // remove the scheduled job - scheduler.removeScheduledJob(eventQueueJobName); - if (queue.isEmpty()) { - log.debug("Event queue was empty on stop"); - } else { - log.debug("Event queue was not empty on stop, processing..."); - do { - processQueue(); - } while (!queue.isEmpty()); - } - recordingConsumer.uninit(); - } else { - log.debug("Recording listener was already stopped"); - } - } - - /** {@inheritDoc} */ - public void packetReceived(IBroadcastStream stream, IStreamPacket packet) { - if (recording.get()) { - // store everything we would need to perform a write of the stream data - CachedEvent event = new CachedEvent(); - event.setData(packet.getData().duplicate()); - event.setDataType(packet.getDataType()); - event.setReceivedTime(System.currentTimeMillis()); - event.setTimestamp(packet.getTimestamp()); - // queue the event - if (!queue.add(event)) { - log.debug("Event packet not added to recording queue"); - } - } else { - log.info("A packet was received by recording listener, but it's not recording anymore. {}", stream.getPublishedName()); - } - } - - /** - * Process the queued items. - */ - private void processQueue() { - CachedEvent cachedEvent; - try { - IRTMPEvent event = null; - RTMPMessage message = null; - // get first event in the queue - cachedEvent = queue.poll(); - if (cachedEvent != null) { - // get the data type - final byte dataType = cachedEvent.getDataType(); - // get the data - IoBuffer buffer = cachedEvent.getData(); - // get the current size of the buffer / data - int bufferLimit = buffer.limit(); - if (bufferLimit > 0) { - // create new RTMP message and push to the consumer - switch (dataType) { - case Constants.TYPE_AGGREGATE: - event = new Aggregate(buffer); - event.setTimestamp(cachedEvent.getTimestamp()); - message = RTMPMessage.build(event); - break; - case Constants.TYPE_AUDIO_DATA: - event = new AudioData(buffer); - event.setTimestamp(cachedEvent.getTimestamp()); - message = RTMPMessage.build(event); - break; - case Constants.TYPE_VIDEO_DATA: - event = new VideoData(buffer); - event.setTimestamp(cachedEvent.getTimestamp()); - message = RTMPMessage.build(event); - break; - default: - event = new Notify(buffer); - event.setTimestamp(cachedEvent.getTimestamp()); - message = RTMPMessage.build(event); - break; - } - // push it down to the recorder - recordingConsumer.pushMessage(null, message); - } else if (bufferLimit == 0 && dataType == Constants.TYPE_AUDIO_DATA) { - log.debug("Stream data size was 0, sending empty audio message"); - // allow for 0 byte audio packets - event = new AudioData(IoBuffer.allocate(0)); - event.setTimestamp(cachedEvent.getTimestamp()); - message = RTMPMessage.build(event); - // push it down to the recorder - recordingConsumer.pushMessage(null, message); - } else { - log.debug("Stream data size was 0, recording pipe will not be notified"); - } - } - } catch (Exception e) { - log.warn("Exception while pushing to consumer", e); - } - } - - /** {@inheritDoc} */ - public boolean isRecording() { - return recording.get(); - } - - /** {@inheritDoc} */ - public boolean isAppending() { - return appending; - } - - /** {@inheritDoc} */ - public FileConsumer getFileConsumer() { - return recordingConsumer; - } - - /** {@inheritDoc} */ - public void setFileConsumer(FileConsumer recordingConsumer) { - this.recordingConsumer = recordingConsumer; - } - - /** {@inheritDoc} */ - public String getFileName() { - return fileName; - } - - /** {@inheritDoc} */ - public void setFileName(String fileName) { - log.debug("File name: {}", fileName); - this.fileName = fileName; - } - - private class EventQueueJob implements IScheduledJob { - - private AtomicBoolean processing = new AtomicBoolean(false); - - public void execute(ISchedulingService service) { - if (processing.compareAndSet(false, true)) { - if (log.isTraceEnabled()) { - log.trace("Event queue size: {}", queue.size()); - } - try { - if (!queue.isEmpty()) { - while (!queue.isEmpty()) { - if (log.isTraceEnabled()) { - log.trace("Taking one more item from queue, size: {}", queue.size()); - } - processQueue(); - } - } else { - log.trace("Nothing to record"); - } - } catch (Exception e) { - log.error("Error processing queue", e); - } finally { - processing.set(false); - } - } - } - - } - -} diff --git a/src/main/java/org/red5/server/stream/SimplePlaylistController.java b/src/main/java/org/red5/server/stream/SimplePlaylistController.java deleted file mode 100644 index 309f7bb30..000000000 --- a/src/main/java/org/red5/server/stream/SimplePlaylistController.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.util.Random; - -import org.red5.server.api.stream.IPlaylist; -import org.red5.server.api.stream.IPlaylistController; - -/** - * Simple playlist controller implementation - */ -public class SimplePlaylistController implements IPlaylistController { - - /** {@inheritDoc} */ - public int nextItem(IPlaylist playlist, int itemIndex) { - if (itemIndex < 0) { - itemIndex = -1; - } - - if (playlist.isRepeat()) { - return itemIndex; - } - - if (playlist.isRandom()) { - int lastIndex = itemIndex; - if (playlist.getItemSize() > 1) { - // continuously generate a random number - // until you get one that was not the last... - Random rand = new Random(); - while (itemIndex == lastIndex) { - itemIndex = rand.nextInt(playlist.getItemSize()); - } - } - return itemIndex; - } - - int nextIndex = itemIndex + 1; - - if (nextIndex < playlist.getItemSize()) { - return nextIndex; - } else if (playlist.isRewind()) { - return playlist.getItemSize() > 0 ? 0 : -1; - } else { - return -1; - } - } - - /** {@inheritDoc} */ - public int previousItem(IPlaylist playlist, int itemIndex) { - - if (itemIndex > playlist.getItemSize()) { - return playlist.getItemSize() - 1; - } - - if (playlist.isRepeat()) { - return itemIndex; - } - - if (playlist.isRandom()) { - Random rand = new Random(); - int lastIndex = itemIndex; - // continuously generate a random number - // until you get one that was not the last... - while (itemIndex == lastIndex) { - itemIndex = rand.nextInt(playlist.getItemSize()); - } - lastIndex = itemIndex; - return itemIndex; - } - - int prevIndex = itemIndex - 1; - - if (prevIndex >= 0) { - return prevIndex; - } else if (playlist.isRewind()) { - return playlist.getItemSize() - 1; - } else { - return -1; - } - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/stream/SingleItemSubscriberStream.java b/src/main/java/org/red5/server/stream/SingleItemSubscriberStream.java deleted file mode 100644 index c5bc680a4..000000000 --- a/src/main/java/org/red5/server/stream/SingleItemSubscriberStream.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -import org.red5.logging.Red5LoggerFactory; -import org.red5.server.api.IConnection; -import org.red5.server.api.IContext; -import org.red5.server.api.Red5; -import org.red5.server.api.scheduling.IScheduledJob; -import org.red5.server.api.scheduling.ISchedulingService; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.stream.IPlayItem; -import org.red5.server.api.stream.ISingleItemSubscriberStream; -import org.red5.server.api.stream.IStreamAwareScopeHandler; -import org.red5.server.api.stream.OperationNotSupportedException; -import org.red5.server.api.stream.StreamState; -import org.slf4j.Logger; - -/** - * Stream of a single play item for a subscriber - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class SingleItemSubscriberStream extends AbstractClientStream implements ISingleItemSubscriberStream { - - private static final Logger log = Red5LoggerFactory.getLogger(SingleItemSubscriberStream.class); - - /** - * Service used to provide notifications, keep client buffer filled, clean up, etc... - */ - protected ISchedulingService schedulingService; - - /** - * Scheduled job names - */ - protected Set jobs = new HashSet(1); - - /** - * Interval in ms to check for buffer underruns in VOD streams. - */ - protected int bufferCheckInterval = 0; - - /** - * Number of pending messages at which a NetStream.Play.InsufficientBW - * message is generated for VOD streams. - */ - protected int underrunTrigger = 10; - - /** - * Timestamp this stream was created. - */ - protected long creationTime = System.currentTimeMillis(); - - private volatile IPlayItem item; - - /** - * Plays items back - */ - protected PlayEngine engine; - - public void setPlayItem(IPlayItem item) { - this.item = item; - } - - public void play() throws IOException { - try { - engine.play(item); - } catch (StreamNotFoundException e) { - //TODO send stream not found to subscriber - } - } - - /** {@inheritDoc} */ - public void pause(int position) { - try { - engine.pause(position); - } catch (IllegalStateException e) { - log.debug("pause caught an IllegalStateException"); - } - } - - /** {@inheritDoc} */ - public void resume(int position) { - try { - engine.resume(position); - } catch (IllegalStateException e) { - log.debug("resume caught an IllegalStateException"); - } - } - - /** {@inheritDoc} */ - public void stop() { - try { - engine.stop(); - } catch (IllegalStateException e) { - log.debug("stop caught an IllegalStateException"); - } - } - - /** {@inheritDoc} */ - public void seek(int position) throws OperationNotSupportedException { - try { - engine.seek(position); - } catch (IllegalStateException e) { - log.debug("seek caught an IllegalStateException"); - } - } - - public boolean isPaused() { - return state == StreamState.PAUSED; - } - - /** {@inheritDoc} */ - public void receiveVideo(boolean receive) { - boolean receiveVideo = engine.receiveVideo(receive); - if (!receiveVideo && receive) { - //video has been re-enabled - seekToCurrentPlayback(); - } - } - - /** {@inheritDoc} */ - public void receiveAudio(boolean receive) { - //check if engine currently receives audio, returns previous value - boolean receiveAudio = engine.receiveAudio(receive); - if (receiveAudio && !receive) { - //send a blank audio packet to reset the player - engine.sendBlankAudio(true); - } else if (!receiveAudio && receive) { - //do a seek - seekToCurrentPlayback(); - } - } - - /** - * Creates a play engine based on current services (scheduling service, consumer service, and provider service). - * This method is useful during unit testing. - */ - PlayEngine createEngine(ISchedulingService schedulingService, IConsumerService consumerService, IProviderService providerService) { - engine = new PlayEngine.Builder(this, schedulingService, consumerService, providerService).build(); - return engine; - } - - /** - * Set interval to check for buffer underruns. Set to 0 to - * disable. - * - * @param bufferCheckInterval interval in ms - */ - public void setBufferCheckInterval(int bufferCheckInterval) { - this.bufferCheckInterval = bufferCheckInterval; - } - - /** - * Set maximum number of pending messages at which a - * NetStream.Play.InsufficientBW message will be - * generated for VOD streams - * - * @param underrunTrigger the maximum number of pending messages - */ - public void setUnderrunTrigger(int underrunTrigger) { - this.underrunTrigger = underrunTrigger; - } - - public void start() { - //ensure the play engine exists - if (engine == null) { - IScope scope = getScope(); - if (scope != null) { - IContext ctx = scope.getContext(); - if (ctx.hasBean(ISchedulingService.BEAN_NAME)) { - schedulingService = (ISchedulingService) ctx.getBean(ISchedulingService.BEAN_NAME); - } else { - //try the parent - schedulingService = (ISchedulingService) scope.getParent().getContext().getBean(ISchedulingService.BEAN_NAME); - } - IConsumerService consumerService = null; - if (ctx.hasBean(IConsumerService.KEY)) { - consumerService = (IConsumerService) ctx.getBean(IConsumerService.KEY); - } else { - //try the parent - consumerService = (IConsumerService) scope.getParent().getContext().getBean(IConsumerService.KEY); - } - IProviderService providerService = null; - if (ctx.hasBean(IProviderService.BEAN_NAME)) { - providerService = (IProviderService) ctx.getBean(IProviderService.BEAN_NAME); - } else { - //try the parent - providerService = (IProviderService) scope.getParent().getContext().getBean(IProviderService.BEAN_NAME); - } - engine = new PlayEngine.Builder(this, schedulingService, consumerService, providerService).build(); - } else { - log.info("Scope was null on start"); - } - } - //set buffer check interval - engine.setBufferCheckInterval(bufferCheckInterval); - //set underrun trigger - engine.setUnderrunTrigger(underrunTrigger); - // Start playback engine - engine.start(); - // Notify subscribers on start - onChange(StreamState.STARTED); - } - - public void close() { - engine.close(); - onChange(StreamState.CLOSED); - // clear jobs - if (schedulingService != null && !jobs.isEmpty()) { - for (String jobName : jobs) { - schedulingService.removeScheduledJob(jobName); - } - jobs.clear(); - } - } - - /** - * {@inheritDoc} - */ - public void onChange(final StreamState state, final Object... changed) { - Notifier notifier = null; - IStreamAwareScopeHandler handler = getStreamAwareHandler(); - switch (state) { - case SEEK: - //notifies subscribers on seek - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get item being played - IPlayItem item = (IPlayItem) changed[0]; - //seek position - int position = (Integer) changed[1]; - try { - handler.streamPlayItemSeek(stream, item, position); - } catch (Throwable t) { - log.error("error notify streamPlayItemSeek", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case PAUSED: - //set the paused state - this.setState(StreamState.PAUSED); - //notifies subscribers on pause - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get item being played - IPlayItem item = (IPlayItem) changed[0]; - //playback position - int position = (Integer) changed[1]; - try { - handler.streamPlayItemPause(stream, item, position); - } catch (Throwable t) { - log.error("error notify streamPlayItemPause", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case RESUMED: - //resume playing - this.setState(StreamState.PLAYING); - //notifies subscribers on resume - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get item being played - IPlayItem item = (IPlayItem) changed[0]; - //playback position - int position = (Integer) changed[1]; - try { - handler.streamPlayItemResume(stream, item, position); - } catch (Throwable t) { - log.error("error notify streamPlayItemResume", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case PLAYING: - //notifies subscribers on play - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get item being played - IPlayItem item = (IPlayItem) changed[0]; - //is it a live broadcast - boolean isLive = (Boolean) changed[1]; - try { - handler.streamPlayItemPlay(stream, item, isLive); - } catch (Throwable t) { - log.error("error notify streamPlayItemPlay", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case CLOSED: - //notifies subscribers on close - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - try { - handler.streamSubscriberClose(stream); - } catch (Throwable t) { - log.error("error notify streamSubscriberClose", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case STARTED: - //notifies subscribers on start - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - try { - handler.streamSubscriberStart(stream); - } catch (Throwable t) { - log.error("error notify streamSubscriberStart", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case STOPPED: - //set the stopped state - this.setState(StreamState.STOPPED); - //notifies subscribers on stop - if (handler != null) { - notifier = new Notifier(this, handler) { - public void execute(ISchedulingService service) { - //make sure those notified have the correct connection - Red5.setConnectionLocal(conn); - //get the item that was stopped - IPlayItem item = (IPlayItem) changed[0]; - try { - handler.streamPlayItemStop(stream, item); - } catch (Throwable t) { - log.error("error notify streamPlaylistItemStop", t); - } - // clear thread local reference - Red5.setConnectionLocal(null); - } - }; - } - break; - case END: - //notified by the play engine when the current item reaches the end - break; - default: - //there is no "default" handling - } - if (notifier != null) { - notifier.setConnection(Red5.getConnectionLocal()); - scheduleOnceJob(notifier); - } - } - - /** - * Seek to current position to restart playback with audio and/or video. - */ - private void seekToCurrentPlayback() { - if (engine.isPullMode()) { - try { - // TODO: figure out if this is the correct position to seek to - final long delta = System.currentTimeMillis() - engine.getPlaybackStart(); - engine.seek((int) delta); - } catch (OperationNotSupportedException err) { - // Ignore error, should not happen for pullMode engines - } - } - } - - /** {@inheritDoc} */ - public String scheduleOnceJob(IScheduledJob job) { - String jobName = schedulingService.addScheduledOnceJob(10, job); - return jobName; - } - - /** {@inheritDoc} */ - public String scheduleWithFixedDelay(IScheduledJob job, int interval) { - String jobName = schedulingService.addScheduledJob(interval, job); - jobs.add(jobName); - return jobName; - } - - /** {@inheritDoc} */ - public void cancelJob(String jobName) { - schedulingService.removeScheduledJob(jobName); - } - - /** - * Handles notifications in a separate thread. - */ - public class Notifier implements IScheduledJob { - - ISingleItemSubscriberStream stream; - - IStreamAwareScopeHandler handler; - - IConnection conn; - - public Notifier(ISingleItemSubscriberStream stream, IStreamAwareScopeHandler handler) { - log.trace("Notifier - stream: {} handler: {}", stream, handler); - this.stream = stream; - this.handler = handler; - } - - public void setConnection(IConnection conn) { - this.conn = conn; - } - - public void execute(ISchedulingService service) { - } - - } - -} diff --git a/src/main/java/org/red5/server/stream/StreamNotFoundException.java b/src/main/java/org/red5/server/stream/StreamNotFoundException.java deleted file mode 100644 index ab144f90b..000000000 --- a/src/main/java/org/red5/server/stream/StreamNotFoundException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -/** - * Throw when stream can't be found - */ -public class StreamNotFoundException extends Exception { - private static final long serialVersionUID = 812106823615971891L; - - public StreamNotFoundException(String name) { - super(String.format("Stream %s not found", name)); - } - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/stream/StreamService.java b/src/main/java/org/red5/server/stream/StreamService.java deleted file mode 100644 index 221e5bfc1..000000000 --- a/src/main/java/org/red5/server/stream/StreamService.java +++ /dev/null @@ -1,751 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.lang3.StringUtils; -import org.red5.io.utils.ObjectMap; -import org.red5.server.BaseConnection; -import org.red5.server.api.IConnection; -import org.red5.server.api.IContext; -import org.red5.server.api.Red5; -import org.red5.server.api.scope.IBroadcastScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.service.IStreamSecurityService; -import org.red5.server.api.stream.IBroadcastStream; -import org.red5.server.api.stream.IClientBroadcastStream; -import org.red5.server.api.stream.IClientStream; -import org.red5.server.api.stream.IPlayItem; -import org.red5.server.api.stream.IPlaylistSubscriberStream; -import org.red5.server.api.stream.ISingleItemSubscriberStream; -import org.red5.server.api.stream.IStreamCapableConnection; -import org.red5.server.api.stream.IStreamPlaybackSecurity; -import org.red5.server.api.stream.IStreamPublishSecurity; -import org.red5.server.api.stream.IStreamService; -import org.red5.server.api.stream.ISubscriberStream; -import org.red5.server.api.stream.OperationNotSupportedException; -import org.red5.server.api.stream.support.DynamicPlayItem; -import org.red5.server.api.stream.support.SimplePlayItem; -import org.red5.server.net.rtmp.Channel; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.net.rtmp.status.Status; -import org.red5.server.net.rtmp.status.StatusCodes; -import org.red5.server.util.ScopeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Stream service - */ -public class StreamService implements IStreamService { - - private static Logger log = LoggerFactory.getLogger(StreamService.class); - - /** - * Use to determine playback type. - */ - private ThreadLocal simplePlayback = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return Boolean.TRUE; - } - }; - - /** {@inheritDoc} */ - public int createStream() { - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - return ((IStreamCapableConnection) conn).reserveStreamId(); - } else { - return -1; - } - } - - /** {@inheritDoc} */ - public void initStream(int streamId) { - IConnection conn = Red5.getConnectionLocal(); - log.info("initStream: id={} current id: {} connection={}", streamId, conn.getStreamId(), conn); - if (conn instanceof IStreamCapableConnection) { - ((IStreamCapableConnection) conn).reserveStreamId(streamId); - IClientStream stream = ((IStreamCapableConnection) conn).getStreamById(streamId); - if (stream != null) { - if (stream instanceof IClientBroadcastStream) { - IClientBroadcastStream bs = (IClientBroadcastStream) stream; - IBroadcastScope bsScope = getBroadcastScope(conn.getScope(), bs.getPublishedName()); - if (bsScope != null && conn instanceof BaseConnection) { - ((BaseConnection) conn).unregisterBasicScope(bsScope); - } - } - stream.close(); - } - ((IStreamCapableConnection) conn).deleteStreamById(streamId); - } else { - log.warn("ERROR in intiStream, connection is not stream capable"); - } - } - - /** - * Close stream - */ - public void closeStream() { - IConnection conn = Red5.getConnectionLocal(); - closeStream(conn, conn.getStreamId()); - } - - /** - * Close stream. - * This method can close both IClientBroadcastStream (coming from Flash Player to Red5) and ISubscriberStream (from Red5 to Flash Player). - * Corresponding application handlers (streamSubscriberClose, etc.) are called as if close was initiated by Flash Player. - * - * It is recommended to remember stream id in application handlers, ex.: - *
-	 * public void streamBroadcastStart(IBroadcastStream stream) {
-	 * 	super.streamBroadcastStart(stream);
-	 * 	if (stream instanceof IClientBroadcastStream) {
-	 * 		int publishedStreamId = ((ClientBroadcastStream)stream).getStreamId();
-	 * 		Red5.getConnectionLocal().setAttribute(PUBLISHED_STREAM_ID_ATTRIBUTE, publishedStreamId);
-	 * 	}
-	 * }
-	 * 
- *
-	 * public void streamPlaylistItemPlay(IPlaylistSubscriberStream stream, IPlayItem item, boolean isLive) {
-	 * 	super.streamPlaylistItemPlay(stream, item, isLive);
-	 * 	Red5.getConnectionLocal().setAttribute(WATCHED_STREAM_ID_ATTRIBUTE, stream.getStreamId());
-	 * }
-	 * 
- * When stream is closed, corresponding NetStream status will be sent to stream provider / consumers. - * Implementation is based on Red5's StreamService.close() - * - * @param conn client connection - * @param streamId stream ID (number: 1,2,...) - */ - public void closeStream(IConnection conn, int streamId) { - log.info("closeStream: streamId={}, connection={}", streamId, conn); - if (conn instanceof IStreamCapableConnection) { - IStreamCapableConnection scConn = (IStreamCapableConnection) conn; - IClientStream stream = scConn.getStreamById(streamId); - if (stream != null) { - if (stream instanceof IClientBroadcastStream) { - // this is a broadcasting stream (from Flash Player to Red5) - IClientBroadcastStream bs = (IClientBroadcastStream) stream; - IBroadcastScope bsScope = getBroadcastScope(conn.getScope(), bs.getPublishedName()); - if (bsScope != null && conn instanceof BaseConnection) { - ((BaseConnection) conn).unregisterBasicScope(bsScope); - } - } - stream.close(); - scConn.deleteStreamById(streamId); - // in case of broadcasting stream, status is sent automatically by Red5 - if (!(stream instanceof IClientBroadcastStream)) { - StreamService.sendNetStreamStatus(conn, StatusCodes.NS_PLAY_STOP, "Stream closed by server", stream.getName(), Status.STATUS, streamId); - } - } else { - log.info("Stream not found: streamId={}, connection={}", streamId, conn); - } - } else { - log.warn("Connection is not instance of IStreamCapableConnection: {}", conn); - } - } - - /** {@inheritDoc} */ - public void releaseStream(String streamName) { - // XXX: what to do here? - } - - /** {@inheritDoc} */ - public void deleteStream(int streamId) { - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - deleteStream(streamConn, streamId); - } - } - - /** {@inheritDoc} */ - public void deleteStream(IStreamCapableConnection conn, int streamId) { - IClientStream stream = conn.getStreamById(streamId); - if (stream != null) { - if (stream instanceof IClientBroadcastStream) { - IClientBroadcastStream bs = (IClientBroadcastStream) stream; - IBroadcastScope bsScope = getBroadcastScope(conn.getScope(), bs.getPublishedName()); - if (bsScope != null && conn instanceof BaseConnection) { - ((BaseConnection) conn).unregisterBasicScope(bsScope); - } - } - stream.close(); - } - conn.unreserveStreamId(streamId); - } - - /** {@inheritDoc} */ - public void pauseRaw(Boolean pausePlayback, int position) { - log.trace("pauseRaw - pausePlayback:{} position:{}", pausePlayback, position); - pause(pausePlayback, position); - } - - /** - * Pause at given position. Required as "pausePlayback" can be "null" if no flag is passed by the - * client - * @param pausePlayback Pause playback or not - * @param position Pause position - */ - public void pause(Boolean pausePlayback, int position) { - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - int streamId = conn.getStreamId(); - IClientStream stream = streamConn.getStreamById(streamId); - if (stream != null && stream instanceof ISubscriberStream) { - ISubscriberStream subscriberStream = (ISubscriberStream) stream; - // pausePlayback can be "null" if "pause" is called without any parameters from flash - if (pausePlayback == null) { - pausePlayback = !subscriberStream.isPaused(); - } - if (pausePlayback) { - subscriberStream.pause(position); - } else { - subscriberStream.resume(position); - } - } - } - } - - /** - * Plays back a stream based on the supplied name, from the specified position for the given length of time. - * - * @param name - The name of a recorded file, or the identifier for live data. If - * @param start - The start time, in seconds. Allowed values are -2, -1, 0, or a positive number. - * The default value is -2, which looks for a live stream, then a recorded stream, and if it finds neither, - * opens a live stream. If -1, plays only a live stream. If 0 or a positive number, plays a recorded stream, - * beginning start seconds in. - * @param length - The duration of the playback, in seconds. Allowed values are -1, 0, or a positive number. - * The default value is -1, which plays a live or recorded stream until it ends. If 0, plays a single frame - * that is start seconds from the beginning of a recorded stream. If a positive number, plays a live or recorded - * stream for length seconds. - * @param reset - Whether to clear a playlist. The default value is 1 or true, which clears any previous play - * calls and plays name immediately. If 0 or false, adds the stream to a playlist. If 2, maintains the playlist - * and returns all stream messages at once, rather than at intervals. If 3, clears the playlist and returns all - * stream messages at once. - */ - public void play(String name, int start, int length, Object reset) { - if (reset instanceof Boolean) { - play(name, start, length, ((Boolean) reset).booleanValue()); - } else { - if (reset instanceof Integer) { - int value = (Integer) reset; - switch (value) { - case 0: - //adds the stream to a playlist - IStreamCapableConnection streamConn = (IStreamCapableConnection) Red5.getConnectionLocal(); - IPlaylistSubscriberStream playlistStream = (IPlaylistSubscriberStream) streamConn.getStreamById(streamConn.getStreamId()); - IPlayItem item = SimplePlayItem.build(name); - playlistStream.addItem(item); - play(name, start, length, false); - break; - case 2: - //maintains the playlist and returns all stream messages at once, rather than at intervals - - break; - case 3: - //clears the playlist and returns all stream messages at once - - break; - default: - //clears any previous play calls and plays name immediately - play(name, start, length, true); - } - } else { - play(name, start, length); - } - } - } - - /** {@inheritDoc} */ - public void play(String name, int start, int length, boolean flushPlaylist) { - log.debug("Play called - name: {} start: {} length: {} flush playlist: {}", new Object[] { name, start, length, flushPlaylist }); - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - IScope scope = conn.getScope(); - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - int streamId = conn.getStreamId(); - if (StringUtils.isEmpty(name)) { - sendNSFailed(streamConn, StatusCodes.NS_FAILED, "The stream name may not be empty.", name, streamId); - return; - } - IStreamSecurityService security = (IStreamSecurityService) ScopeUtils.getScopeService(scope, IStreamSecurityService.class); - if (security != null) { - Set handlers = security.getStreamPlaybackSecurity(); - for (IStreamPlaybackSecurity handler : handlers) { - if (!handler.isPlaybackAllowed(scope, name, start, length, flushPlaylist)) { - sendNSFailed(streamConn, StatusCodes.NS_FAILED, "You are not allowed to play the stream.", name, streamId); - return; - } - } - } - boolean created = false; - IClientStream stream = streamConn.getStreamById(streamId); - if (stream == null) { - if (streamId <= 0) { - streamId = streamConn.reserveStreamId(); - } - stream = streamConn.newPlaylistSubscriberStream(streamId); - stream.setBroadcastStreamPublishName(name); - stream.start(); - created = true; - } - if (stream != null && stream instanceof ISubscriberStream) { - ISubscriberStream subscriberStream = (ISubscriberStream) stream; - IPlayItem item = simplePlayback.get() ? SimplePlayItem.build(name, start, length) : DynamicPlayItem.build(name, start, length); - if (subscriberStream instanceof IPlaylistSubscriberStream) { - IPlaylistSubscriberStream playlistStream = (IPlaylistSubscriberStream) subscriberStream; - if (flushPlaylist) { - playlistStream.removeAllItems(); - } - playlistStream.addItem(item); - } else if (subscriberStream instanceof ISingleItemSubscriberStream) { - ISingleItemSubscriberStream singleStream = (ISingleItemSubscriberStream) subscriberStream; - singleStream.setPlayItem(item); - } else { - // not supported by this stream service - log.warn("Stream instance type: {} is not supported", subscriberStream.getClass().getName()); - return; - } - try { - subscriberStream.play(); - } catch (IOException err) { - if (created) { - stream.close(); - streamConn.deleteStreamById(streamId); - } - sendNSFailed(streamConn, StatusCodes.NS_FAILED, err.getMessage(), name, streamId); - } - } - } else { - log.debug("Connection was not stream capable"); - } - } - - /** {@inheritDoc} */ - public void play(String name, int start, int length) { - play(name, start, length, true); - } - - /** {@inheritDoc} */ - public void play(String name, int start) { - play(name, start, -1000, true); - } - - /** {@inheritDoc} */ - public void play(String name) { - play(name, -2000, -1000, true); - } - - /** {@inheritDoc} */ - public void play(Boolean dontStop) { - log.debug("Play called. Dont stop param: {}", dontStop); - if (!dontStop) { - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - int streamId = conn.getStreamId(); - IClientStream stream = streamConn.getStreamById(streamId); - if (stream != null) { - stream.stop(); - } - } - } - } - - /** - * Dynamic streaming play method. This is a convenience method. - * - * @param oldStreamName - * @param start - * @param transition - * @param length - * @param offset - * @param streamName - */ - public void play2(String oldStreamName, int start, String transition, int length, double offset, String streamName) { - Map playOptions = new HashMap(); - playOptions.put("oldStreamName", oldStreamName); - playOptions.put("streamName", streamName); - playOptions.put("start", start); - playOptions.put("len", length); - playOptions.put("offset", offset); - play2(playOptions); - } - - /** - * Dynamic streaming play method. This is a convenience method. - * - * @param params - */ - @SuppressWarnings("rawtypes") - public void play2(ObjectMap params) { - log.debug("play2 options: {}", params); - Map playOptions = new HashMap(); - for (Object key : params.keySet()) { - String k = key.toString(); - log.trace("Parameter: {}", k); - playOptions.put(k, params.get(k)); - } - play2(playOptions); - } - - /** - * Dynamic streaming play method. - * - * The following properties are supported on the play options: - *
-		streamName: String. The name of the stream to play or the new stream to switch to.
-		oldStreamName: String. The name of the initial stream that needs to be switched out. This is not needed and ignored 
-		               when play2 is used for just playing the stream and not switching to a new stream.
-		start: Number. The start time of the new stream to play, just as supported by the existing play API. and it has the 
-		               same defaults. This is ignored when the method is called for switching (in other words, the transition 
-		               is either NetStreamPlayTransition.SWITCH or NetStreamPlayTransitions.SWAP)
-		len: Number. The duration of the playback, just as supported by the existing play API and has the same defaults.
-		transition: String. The transition mode for the playback command. It could be one of the following:
-							NetStreamPlayTransitions.RESET
-							NetStreamPlayTransitions.APPEND
-							NetStreamPlayTransitions.SWITCH
-							NetStreamPlayTransitions.SWAP
-		
- NetStreamPlayTransitions: -
					
-			APPEND : String = "append" - Adds the stream to a playlist and begins playback with the first stream.
-	 		APPEND_AND_WAIT : String = "appendAndWait" - Builds a playlist without starting to play it from the first stream.
-	 		RESET : String = "reset" - Clears any previous play calls and plays the specified stream immediately.
-	 		RESUME : String = "resume" - Requests data from the new connection starting from the point at which the previous connection ended.
-	 		STOP : String = "stop" - Stops playing the streams in a playlist.
-	 		SWAP : String = "swap" - Replaces a content stream with a different content stream and maintains the rest of the playlist.
-	 		SWITCH : String = "switch" - Switches from playing one stream to another stream, typically with streams of the same content.			
-	   
- @see ActionScript guide to dynamic streaming - @see Dynamic streaming in Flash Media Server - Part 1: Overview of the new capabilities - @see NetStreamPlayTransitions - */ - public void play2(Map playOptions) { - log.debug("play2 options: {}", playOptions.toString()); - /* { streamName=streams/new.flv, oldStreamName=streams/old.flv, - start=0, len=-1, offset=12.195, transition=switch } */ - // get the transition type - String transition = (String) playOptions.get("transition"); - String streamName = (String) playOptions.get("streamName"); - String oldStreamName = (String) playOptions.get("oldStreamName"); - // now initiate new playback - int start = (Integer) playOptions.get("start"); - int length = (Integer) playOptions.get("len"); - // get the clients connection - IConnection conn = Red5.getConnectionLocal(); - if (conn != null && conn instanceof IStreamCapableConnection) { - // get the stream id - int streamId = conn.getStreamId(); - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - if ("stop".equals(transition)) { - play(Boolean.FALSE); - } else if ("reset".equals(transition)) { - // just reset the currently playing stream - play(streamName); - } else if ("switch".equals(transition)) { - try { - // set the playback type - simplePlayback.set(Boolean.FALSE); - // send the "start" of transition - sendNSStatus(conn, StatusCodes.NS_PLAY_TRANSITION, String.format("Transitioning from %s to %s.", oldStreamName, streamName), streamName, streamId); - // support offset? - //playOptions.get("offset") - play(streamName, start, length); - } finally { - // clean up - simplePlayback.remove(); - } - } else if ("append".equals(transition) || "appendAndWait".equals(transition)) { - IPlaylistSubscriberStream playlistStream = (IPlaylistSubscriberStream) streamConn.getStreamById(streamId); - IPlayItem item = SimplePlayItem.build(streamName); - playlistStream.addItem(item); - if ("append".equals(transition)) { - play(streamName, start, length, false); - } - } else if ("swap".equals(transition)) { - IPlaylistSubscriberStream playlistStream = (IPlaylistSubscriberStream) streamConn.getStreamById(streamId); - IPlayItem item = SimplePlayItem.build(streamName); - int itemCount = playlistStream.getItemSize(); - for (int i = 0; i < itemCount; i++) { - IPlayItem tmpItem = playlistStream.getItem(i); - if (tmpItem.getName().equals(oldStreamName)) { - if (!playlistStream.replace(tmpItem, item)) { - log.warn("Playlist item replacement failed"); - sendNSFailed(streamConn, StatusCodes.NS_PLAY_FAILED, "Playlist swap failed.", streamName, streamId); - } - break; - } - } - } else { - log.warn("Unhandled transition: {}", transition); - sendNSFailed(conn, StatusCodes.NS_FAILED, "Transition type not supported", streamName, streamId); - } - } else { - log.info("Connection was null ?"); - } - } - - /** {@inheritDoc} */ - public void publish(Boolean dontStop) { - if (!dontStop) { - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - int streamId = conn.getStreamId(); - IClientStream stream = streamConn.getStreamById(streamId); - if (stream instanceof IBroadcastStream) { - IBroadcastStream bs = (IBroadcastStream) stream; - if (bs.getPublishedName() != null) { - IBroadcastScope bsScope = getBroadcastScope(conn.getScope(), bs.getPublishedName()); - if (bsScope != null) { - bsScope.unsubscribe(bs.getProvider()); - if (conn instanceof BaseConnection) { - ((BaseConnection) conn).unregisterBasicScope(bsScope); - } - } - bs.close(); - streamConn.deleteStreamById(streamId); - } - } - } - } - } - - /** {@inheritDoc} */ - public void publish(String name, String mode) { - Map params = null; - if (name != null && name.contains("?")) { - // read and utilize the query string values - params = new HashMap(); - String tmp = name; - // check if we start with '?' or not - if (name.charAt(0) != '?') { - tmp = name.split("\\?")[1]; - } else if (name.charAt(0) == '?') { - tmp = name.substring(1); - } - // now break up into key/value blocks - String[] kvs = tmp.split("&"); - // take each key/value block and break into its key value parts - for (String kv : kvs) { - String[] split = kv.split("="); - params.put(split[0], split[1]); - } - // grab the streams name - name = name.substring(0, name.indexOf("?")); - } - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - IScope scope = conn.getScope(); - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - int streamId = conn.getStreamId(); - if (StringUtils.isEmpty(name)) { - sendNSFailed(streamConn, StatusCodes.NS_FAILED, "The stream name may not be empty.", name, streamId); - return; - } - IStreamSecurityService security = (IStreamSecurityService) ScopeUtils.getScopeService(scope, IStreamSecurityService.class); - if (security != null) { - Set handlers = security.getStreamPublishSecurity(); - for (IStreamPublishSecurity handler : handlers) { - if (!handler.isPublishAllowed(scope, name, mode)) { - sendNSFailed(streamConn, StatusCodes.NS_PUBLISH_BADNAME, "You are not allowed to publish the stream.", name, streamId); - return; - } - } - } - IBroadcastScope bsScope = getBroadcastScope(scope, name); - if (bsScope != null && !bsScope.getProviders().isEmpty()) { - // another stream with that name is already published - sendNSFailed(streamConn, StatusCodes.NS_PUBLISH_BADNAME, name, name, streamId); - return; - } - IClientStream stream = streamConn.getStreamById(streamId); - if (stream != null && !(stream instanceof IClientBroadcastStream)) { - return; - } - boolean created = false; - if (stream == null) { - stream = streamConn.newBroadcastStream(streamId); - created = true; - } - IClientBroadcastStream bs = (IClientBroadcastStream) stream; - try { - // set publish name - bs.setPublishedName(name); - // set stream parameters if they exist - if (params != null) { - bs.setParameters(params); - } - IContext context = conn.getScope().getContext(); - IProviderService providerService = (IProviderService) context.getBean(IProviderService.BEAN_NAME); - // TODO handle registration failure - if (providerService.registerBroadcastStream(conn.getScope(), name, bs)) { - bsScope = getBroadcastScope(conn.getScope(), name); - bsScope.setClientBroadcastStream(bs); - if (conn instanceof BaseConnection) { - ((BaseConnection) conn).registerBasicScope(bsScope); - } - } - log.debug("Mode: {}", mode); - if (IClientStream.MODE_RECORD.equals(mode)) { - bs.start(); - bs.saveAs(name, false); - } else if (IClientStream.MODE_APPEND.equals(mode)) { - bs.start(); - bs.saveAs(name, true); - } else { - bs.start(); - } - bs.startPublishing(); - } catch (IOException e) { - log.warn("Stream I/O exception", e); - sendNSFailed(streamConn, StatusCodes.NS_RECORD_NOACCESS, "The file could not be created/written to.", name, streamId); - bs.close(); - if (created) { - streamConn.deleteStreamById(streamId); - } - } catch (Exception e) { - log.warn("Exception on publish", e); - } - } - } - - /** {@inheritDoc} */ - public void publish(String name) { - publish(name, IClientStream.MODE_LIVE); - } - - /** {@inheritDoc} */ - public void seek(int position) { - log.trace("seek - position:{}", position); - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - int streamId = conn.getStreamId(); - IClientStream stream = streamConn.getStreamById(streamId); - if (stream != null && stream instanceof ISubscriberStream) { - ISubscriberStream subscriberStream = (ISubscriberStream) stream; - try { - subscriberStream.seek(position); - } catch (OperationNotSupportedException err) { - sendNSFailed(streamConn, StatusCodes.NS_SEEK_FAILED, "The stream doesn't support seeking.", stream.getName(), streamId); - } - } - } - } - - /** {@inheritDoc} */ - public void receiveVideo(boolean receive) { - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - int streamId = conn.getStreamId(); - IClientStream stream = streamConn.getStreamById(streamId); - if (stream != null && stream instanceof ISubscriberStream) { - ISubscriberStream subscriberStream = (ISubscriberStream) stream; - subscriberStream.receiveVideo(receive); - } - } - } - - /** {@inheritDoc} */ - public void receiveAudio(boolean receive) { - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; - int streamId = conn.getStreamId(); - IClientStream stream = streamConn.getStreamById(streamId); - if (stream != null && stream instanceof ISubscriberStream) { - ISubscriberStream subscriberStream = (ISubscriberStream) stream; - subscriberStream.receiveAudio(receive); - } - } - } - - /** - * Return broadcast scope object for given scope and child scope name. - * - * @param scope Scope object - * @param name Child scope name - * @return Broadcast scope - */ - public IBroadcastScope getBroadcastScope(IScope scope, String name) { - return scope.getBroadcastScope(name); - } - - /** - * Send a NetStream.Play.Failed to the client. - * - * @param conn - * @param errorCode - * @param description - * @param name - * @param streamId - */ - private void sendNSFailed(IConnection conn, String errorCode, String description, String name, int streamId) { - StreamService.sendNetStreamStatus(conn, errorCode, description, name, Status.ERROR, streamId); - } - - /** - * Send NetStream.Status to the client. - * @param conn - * @param statusCode see StatusCodes class - * @param description - * @param name - * @param streamId - */ - private void sendNSStatus(IConnection conn, String statusCode, String description, String name, int streamId) { - StreamService.sendNetStreamStatus(conn, statusCode, description, name, Status.STATUS, streamId); - } - - /** - * Send NetStream.Status to the client. - * - * @param conn connection - * @param statusCode NetStream status code - * @param description description - * @param name name - * @param status The status - error, warning, or status - * @param streamId stream id - */ - public static void sendNetStreamStatus(IConnection conn, String statusCode, String description, String name, String status, int streamId) { - if (conn instanceof RTMPConnection) { - Status s = new Status(statusCode); - s.setClientid(streamId); - s.setDesciption(description); - s.setDetails(name); - s.setLevel(status); - // get the channel - Channel channel = ((RTMPConnection) conn).getChannel((byte) (4 + ((streamId - 1) * 5))); - channel.sendStatus(s); - } else { - throw new RuntimeException("Connection is not RTMPConnection: " + conn); - } - } - -} diff --git a/src/main/java/org/red5/server/stream/StreamableFileFactory.java b/src/main/java/org/red5/server/stream/StreamableFileFactory.java deleted file mode 100644 index 865c0ecf4..000000000 --- a/src/main/java/org/red5/server/stream/StreamableFileFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.io.File; -import java.util.HashSet; -import java.util.Set; - -import org.red5.server.api.service.IStreamableFileService; -import org.red5.server.api.stream.IStreamableFileFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Creates streamable file services - */ -public class StreamableFileFactory implements IStreamableFileFactory { - - // Initialize Logging - public static Logger logger = LoggerFactory.getLogger(StreamableFileFactory.class); - - private Set services = new HashSet(); - - /** - * Setter for services - * - * @param services Set of streamable file services - */ - public void setServices(Set services) { - logger.debug("StreamableFileFactory set services"); - this.services = services; - } - - /** {@inheritDoc} */ - public IStreamableFileService getService(File fp) { - logger.debug("Get service for file: " + fp.getName()); - // Return first service that can handle the passed file - for (IStreamableFileService service : this.services) { - if (service.canHandle(fp)) { - logger.debug("Found service"); - return service; - } - } - return null; - } - - /** {@inheritDoc} */ - public Set getServices() { - logger.debug("StreamableFileFactory get services"); - return services; - } -} diff --git a/src/main/java/org/red5/server/stream/VideoCodecFactory.java b/src/main/java/org/red5/server/stream/VideoCodecFactory.java deleted file mode 100644 index a2890ced8..000000000 --- a/src/main/java/org/red5/server/stream/VideoCodecFactory.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.codec.IVideoStreamCodec; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Factory for video codecs. Creates and returns video codecs - * - * @author The Red5 Project - * @author Paul Gregoire (mondain@gmail.com) - */ -public class VideoCodecFactory { - /** - * Object key - */ - public static final String KEY = "videoCodecFactory"; - - /** - * Logger for video factory - */ - private static Logger log = LoggerFactory.getLogger(VideoCodecFactory.class); - - /** - * List of available codecs - */ - private static List codecs = new ArrayList(3); - - /** - * Setter for codecs - * - * @param codecs List of codecs - */ - public void setCodecs(List codecs) { - VideoCodecFactory.codecs = codecs; - } - - /** - * Create and return new video codec applicable for byte buffer data - * @param data Byte buffer data - * @return Video codec - */ - public static IVideoStreamCodec getVideoCodec(IoBuffer data) { - IVideoStreamCodec result = null; - //get the codec identifying byte - int codecId = data.get() & 0x0f; - try { - switch (codecId) { - case 2: //sorenson - result = (IVideoStreamCodec) Class.forName("org.red5.codec.SorensonVideo").newInstance(); - break; - case 3: //screen video - result = (IVideoStreamCodec) Class.forName("org.red5.codec.ScreenVideo").newInstance(); - break; - case 6: //screen video 2 - result = (IVideoStreamCodec) Class.forName("org.red5.codec.ScreenVideo2").newInstance(); - break; - case 7: //avc/h.264 video - result = (IVideoStreamCodec) Class.forName("org.red5.codec.AVCVideo").newInstance(); - break; - } - } catch (Exception ex) { - log.error("Error creating codec instance", ex); - } - data.rewind(); - //if codec is not found do the old-style loop - if (result == null) { - for (IVideoStreamCodec storedCodec: codecs) { - IVideoStreamCodec codec; - // XXX: this is a bit of a hack to create new instances of the - // configured video codec for each stream - try { - codec = storedCodec.getClass().newInstance(); - } catch (Exception e) { - log.error("Could not create video codec instance", e); - continue; - } - if (codec.canHandleData(data)) { - result = codec; - break; - } - } - } - return result; - } - -// private boolean isScreenVideo(byte first) { -// log.debug("Trying ScreenVideo"); -// boolean result = ((first & 0x0f) == 3); -// return result; -// } -// -// private boolean isSorenson(byte first) { -// log.debug("Trying Sorenson"); -// boolean result = ((first & 0x0f) == 2); -// return result; -// } - -} diff --git a/src/main/java/org/red5/server/stream/VideoFrameDropper.java b/src/main/java/org/red5/server/stream/VideoFrameDropper.java deleted file mode 100644 index e017efb19..000000000 --- a/src/main/java/org/red5/server/stream/VideoFrameDropper.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream; - -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.net.rtmp.event.VideoData.FrameType; -import org.red5.server.stream.message.RTMPMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * State machine for video frame dropping in live streams. - *

- * We start sending all frame types. Disposable interframes can be dropped any - * time without affecting the current state. If a regular interframe is dropped, - * all future frames up to the next keyframes are dropped as well. Dropped - * keyframes result in only keyframes being sent. If two consecutive keyframes - * have been successfully sent, regular interframes will be sent in the next - * iteration as well. If these frames all went through, disposable interframes - * are sent again. - * - *

- * So from highest to lowest bandwidth and back, the states go as follows: - *

    - *
  • all frames
  • - *
  • keyframes and interframes
  • - *
  • keyframes
  • - *
  • keyframes and interframes
  • - *
  • all frames
  • - *
- * - * @author The Red5 Project - * @author Joachim Bauch (jojo@struktur.de) - */ -public class VideoFrameDropper implements IFrameDropper { - - protected static Logger log = LoggerFactory.getLogger(VideoFrameDropper.class.getName()); - - /** Current state. */ - private int state; - - /** Constructs a new VideoFrameDropper. */ - public VideoFrameDropper() { - reset(); - } - - /** {@inheritDoc} */ - public void reset() { - reset(SEND_ALL); - } - - /** {@inheritDoc} */ - public void reset(int state) { - this.state = state; - } - - /** {@inheritDoc} */ - public boolean canSendPacket(RTMPMessage message, long pending) { - IRTMPEvent packet = message.getBody(); - boolean result = true; - // We currently only drop video packets. - if (packet instanceof VideoData) { - VideoData video = (VideoData) packet; - FrameType type = video.getFrameType(); - switch (state) { - case SEND_ALL: - // All packets will be sent - break; - case SEND_INTERFRAMES: - // Only keyframes and interframes will be sent. - if (type == FrameType.KEYFRAME) { - if (pending == 0) { - // Send all frames from now on. - state = SEND_ALL; - } - } else if (type == FrameType.INTERFRAME) { - } - break; - case SEND_KEYFRAMES: - // Only keyframes will be sent. - result = (type == FrameType.KEYFRAME); - if (result && pending == 0) { - // Maybe switch back to SEND_INTERFRAMES after the next keyframe - state = SEND_KEYFRAMES_CHECK; - } - break; - case SEND_KEYFRAMES_CHECK: - // Only keyframes will be sent. - result = (type == FrameType.KEYFRAME); - if (result && pending == 0) { - // Continue with sending interframes as well - state = SEND_INTERFRAMES; - } - break; - default: - } - } - return result; - } - - /** {@inheritDoc} */ - public void dropPacket(RTMPMessage message) { - IRTMPEvent packet = message.getBody(); - // Only check video packets. - if (packet instanceof VideoData) { - VideoData video = (VideoData) packet; - FrameType type = video.getFrameType(); - switch (state) { - case SEND_ALL: - if (type == FrameType.DISPOSABLE_INTERFRAME) { - // Remain in state, packet is safe to drop. - return; - } else if (type == FrameType.INTERFRAME) { - // Drop all frames until the next keyframe. - state = SEND_KEYFRAMES; - return; - } else if (type == FrameType.KEYFRAME) { - // Drop all frames until the next keyframe. - state = SEND_KEYFRAMES; - return; - } - break; - case SEND_INTERFRAMES: - if (type == FrameType.INTERFRAME) { - // Drop all frames until the next keyframe. - state = SEND_KEYFRAMES_CHECK; - return; - } else if (type == FrameType.KEYFRAME) { - // Drop all frames until the next keyframe. - state = SEND_KEYFRAMES; - return; - } - break; - case SEND_KEYFRAMES: - // Remain in state. - break; - case SEND_KEYFRAMES_CHECK: - if (type == FrameType.KEYFRAME) { - // Switch back to sending keyframes, but don't move to - // SEND_INTERFRAMES afterwards. - state = SEND_KEYFRAMES; - return; - } - break; - default: - } - } - } - - /** {@inheritDoc} */ - public void sendPacket(RTMPMessage message) { - - } - -} diff --git a/src/main/java/org/red5/server/stream/bandwidth/ClientServerDetection.java b/src/main/java/org/red5/server/stream/bandwidth/ClientServerDetection.java deleted file mode 100644 index a399a45f3..000000000 --- a/src/main/java/org/red5/server/stream/bandwidth/ClientServerDetection.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream.bandwidth; - -import java.util.HashMap; -import java.util.Map; - -import org.red5.server.api.IConnection; -import org.red5.server.api.Red5; -import org.red5.server.api.service.IPendingServiceCall; -import org.red5.server.api.service.IPendingServiceCallback; -import org.red5.server.api.stream.IStreamCapableConnection; -import org.red5.server.service.Call; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * - * @author The Red5 Project - * @author Dan Rossi - */ -public class ClientServerDetection implements IPendingServiceCallback { - - protected static Logger log = LoggerFactory.getLogger(ClientServerDetection.class); - - /** - * Handle callback from service call. - */ - public void resultReceived(IPendingServiceCall call) { - // if we aren't connection, skip any further testing - if (Call.STATUS_NOT_CONNECTED != call.getStatus()) { - - } else { - log.debug("Pending call skipped due to being no longer connected"); - } - } - - private IStreamCapableConnection getStats() { - IConnection conn = Red5.getConnectionLocal(); - if (conn instanceof IStreamCapableConnection) { - return (IStreamCapableConnection) conn; - } - return null; - } - - public Map checkBandwidth(Object[] params) { - final IStreamCapableConnection stats = getStats(); - Map statsValues = new HashMap(); - Integer time = (Integer) (params.length > 0 ? params[0] : 0); - statsValues.put("cOutBytes", stats.getReadBytes()); - statsValues.put("cInBytes", stats.getWrittenBytes()); - statsValues.put("time", time); - log.debug("cOutBytes: {} cInBytes: {} time: {}", new Object[] { stats.getReadBytes(), stats.getWrittenBytes(), time }); - return statsValues; - } - -} diff --git a/src/main/java/org/red5/server/stream/bandwidth/IBandwidthDetection.java b/src/main/java/org/red5/server/stream/bandwidth/IBandwidthDetection.java deleted file mode 100644 index 9b9a1fb20..000000000 --- a/src/main/java/org/red5/server/stream/bandwidth/IBandwidthDetection.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream.bandwidth; - -import org.red5.server.api.IConnection; - -/** - * - * @author The Red5 Project - * @author Dan Rossi - */ -public interface IBandwidthDetection { - - public void checkBandwidth(IConnection conn); - - public void calculateClientBw(IConnection conn); - -} \ No newline at end of file diff --git a/src/main/java/org/red5/server/stream/bandwidth/ServerClientDetection.java b/src/main/java/org/red5/server/stream/bandwidth/ServerClientDetection.java deleted file mode 100644 index f8a496453..000000000 --- a/src/main/java/org/red5/server/stream/bandwidth/ServerClientDetection.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream.bandwidth; - -import java.util.HashMap; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; - -import org.red5.server.api.IConnection; -import org.red5.server.api.Red5; -import org.red5.server.api.service.IPendingServiceCall; -import org.red5.server.api.service.IPendingServiceCallback; -import org.red5.server.api.service.IServiceCapableConnection; -import org.red5.server.service.Call; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Calculates the bandwidth between the client and server. The checks originate from the server. - * - * @see FMS 3.5 Bandwidth Doc - * - * @author The Red5 Project - * @author Dan Rossi - * @author Paul Gregoire - */ -public class ServerClientDetection implements IPendingServiceCallback, IBandwidthDetection { - - private static Logger log = LoggerFactory.getLogger(ServerClientDetection.class); - - // maximum latency alloted for in milliseconds - private static final double LATENCY_MAX = 1000d; - - // minimum latency alloted for in milliseconds - private static final double LATENCY_MIN = 10d; - - private IConnection conn; - - private volatile double latency; - - private volatile double cumLatency = 1; - - private double kbitDown; - - private double deltaDown; - - private double deltaTime; - - // current bytes written on the connection - private long startBytesWritten; - - // start time using nanos - private long startTime; - - // time passed overall - private long timePassed; - - private AtomicInteger packetsSent = new AtomicInteger(0); - - private AtomicInteger packetsReceived = new AtomicInteger(0); - - private byte[] payload = new byte[1024]; - - private byte[] payload1 = new byte[1024 * 32]; - - public void checkBandwidth(IConnection conn) { - calculateClientBw(conn); - } - - public void calculateClientBw(IConnection conn) { - log.debug("calculateClientBw: {} ", conn); - // set local connection ref - this.conn = conn; - // get random generator - Random rnd = new Random(); - rnd.nextBytes(payload); - rnd.nextBytes(payload1); - // get the current bytes written on the connection - startBytesWritten = conn.getWrittenBytes(); - // start time using nanos - startTime = System.nanoTime(); - log.debug("Starting bandwidth check at {} ns", startTime); - callBWCheck(""); - } - - /** - * Handle callback from service call. - */ - public void resultReceived(IPendingServiceCall call) { - // if we aren't connection, skip any further testing - if (Call.STATUS_NOT_CONNECTED != call.getStatus()) { - // receive time using nanos - long now = System.nanoTime(); - // increment received - int received = packetsReceived.incrementAndGet(); - log.debug("Call time stamps - write: {} read: {}", call.getWriteTime(), call.getReadTime()); - // time passed is in milliseconds - timePassed = (now - startTime) / 1000000; - log.debug("Received count: {} sent: {} timePassed: {} ms", new Object[] { received, packetsSent.get(), timePassed }); - switch (received) { - case 1: - // first packet is used to test latency - latency = Math.max(Math.min(timePassed, LATENCY_MAX), LATENCY_MIN); - log.debug("Receive latency: {}", latency); - // We now have a latency figure so can start sending test data. - // Second call. 1st packet sent - log.debug("Sending first payload at {} ns", now); - callBWCheck(payload); // 1k - break; - case 2: - log.debug("Sending second payload at {} ns", now); - // increment cumulative latency - cumLatency++; - callBWCheck(payload1); // 32k - break; - default: - log.debug("Doing calculations at {} ns", now); - // increment cumulative latency - cumLatency++; - // bytes to kbits - deltaDown = ((conn.getWrittenBytes() - startBytesWritten) * 8) / 1000d; - log.debug("Delta kbits: {}", deltaDown); - // total dl time - latency for each packet sent in secs - deltaTime = (timePassed - (latency * cumLatency)); - if (deltaTime <= 0) { - deltaTime = (timePassed + latency); - } - log.debug("Delta time: {} ms", deltaTime); - // calculate kbit/s - kbitDown = Math.round(deltaDown / (deltaTime / 1000d)); - log.debug("onBWDone: kbitDown: {} deltaDown: {} deltaTime: {} latency: {} ", new Object[] { kbitDown, deltaDown, deltaTime, latency }); - callBWDone(); - } - } else { - log.debug("Pending call skipped due to being no longer connected"); - } - } - - private void callBWCheck(Object payload) { - if (log.isTraceEnabled()) { - log.trace("callBWCheck: {}", payload); - } else { - log.debug("callBWCheck"); - } - IConnection conn = Red5.getConnectionLocal(); - Map statsValues = new HashMap(); - statsValues.put("count", packetsReceived.get()); - statsValues.put("sent", packetsSent.get()); - statsValues.put("timePassed", timePassed); - statsValues.put("latency", latency); - statsValues.put("cumLatency", cumLatency); - statsValues.put("payload", payload); - if (conn instanceof IServiceCapableConnection) { - log.debug("Invoking onBWCheck on the client"); - // increment sent counter - packetsSent.incrementAndGet(); - // invoke on the client - ((IServiceCapableConnection) conn).invoke("onBWCheck", new Object[] { statsValues }, this); - } - } - - private void callBWDone() { - log.debug("callBWDone"); - IConnection conn = Red5.getConnectionLocal(); - Map statsValues = new HashMap(); - statsValues.put("kbitDown", kbitDown); - statsValues.put("deltaDown", deltaDown); - statsValues.put("deltaTime", deltaTime); - statsValues.put("latency", latency); - if (conn instanceof IServiceCapableConnection) { - log.debug("Invoking onBWDone on the client"); - // invoke on the client - ((IServiceCapableConnection) conn).invoke("onBWDone", new Object[] { statsValues }); - // adjust bandwidth to mbit/s - int mbits = (int) ((kbitDown / 1000d) * 1000000); - log.debug("Setting bandwidth to {} mbit/s", mbits); - // tell the flash player how fast we want data and how fast we shall send it - conn.setBandwidth(mbits); - } - } - - public void onServerClientBWCheck() { - log.debug("onServerClientBWCheck"); - calculateClientBw(Red5.getConnectionLocal()); - } - -} diff --git a/src/main/java/org/red5/server/stream/consumer/ConnectionConsumer.java b/src/main/java/org/red5/server/stream/consumer/ConnectionConsumer.java deleted file mode 100644 index c2644d91c..000000000 --- a/src/main/java/org/red5/server/stream/consumer/ConnectionConsumer.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream.consumer; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.server.api.stream.IClientStream; -import org.red5.server.messaging.IMessage; -import org.red5.server.messaging.IMessageComponent; -import org.red5.server.messaging.IPipe; -import org.red5.server.messaging.IPipeConnectionListener; -import org.red5.server.messaging.IPushableConsumer; -import org.red5.server.messaging.OOBControlMessage; -import org.red5.server.messaging.PipeConnectionEvent; -import org.red5.server.net.rtmp.Channel; -import org.red5.server.net.rtmp.RTMPConnection; -import org.red5.server.net.rtmp.event.AudioData; -import org.red5.server.net.rtmp.event.BytesRead; -import org.red5.server.net.rtmp.event.ChunkSize; -import org.red5.server.net.rtmp.event.FlexStreamSend; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.Notify; -import org.red5.server.net.rtmp.event.Ping; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.net.rtmp.message.Header; -import org.red5.server.stream.message.RTMPMessage; -import org.red5.server.stream.message.ResetMessage; -import org.red5.server.stream.message.StatusMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RTMP connection consumer. - */ -public class ConnectionConsumer implements IPushableConsumer, IPipeConnectionListener { - - private static final Logger log = LoggerFactory.getLogger(ConnectionConsumer.class); - - /** - * Connection consumer class name - */ - public static final String KEY = ConnectionConsumer.class.getName(); - - /** - * Connection object - */ - private RTMPConnection conn; - - /** - * Video channel - */ - private Channel video; - - /** - * Audio channel - */ - private Channel audio; - - /** - * Data channel - */ - private Channel data; - - /** - * Chunk size. Packets are sent chunk-by-chunk. - */ - private int chunkSize = 1024; //TODO: Not sure of the best value here - - /** - * Whether or not the chunk size has been sent. This seems to be - * required for h264. - */ - private boolean chunkSizeSent; - - /** - * Create RTMP connection consumer for given connection and channels. - * - * @param conn RTMP connection - * @param videoChannel Video channel - * @param audioChannel Audio channel - * @param dataChannel Data channel - */ - public ConnectionConsumer(RTMPConnection conn, Channel videoChannel, Channel audioChannel, Channel dataChannel) { - log.debug("Channel ids - video: {} audio: {} data: {}", new Object[] { videoChannel, audioChannel, dataChannel }); - this.conn = conn; - this.video = videoChannel; - this.audio = audioChannel; - this.data = dataChannel; - } - - /** - * Create connection consumer without an RTMP connection. - * - * @param videoChannel - * @param audioChannel - * @param dataChannel - */ - public ConnectionConsumer(Channel videoChannel, Channel audioChannel, Channel dataChannel) { - this.video = videoChannel; - this.audio = audioChannel; - this.data = dataChannel; - } - - /** {@inheritDoc} */ - public void pushMessage(IPipe pipe, IMessage message) { - //log.trace("pushMessage - type: {}", message.getMessageType()); - if (message instanceof ResetMessage) { - //ignore - } else if (message instanceof StatusMessage) { - StatusMessage statusMsg = (StatusMessage) message; - data.sendStatus(statusMsg.getBody()); - } else if (message instanceof RTMPMessage) { - //make sure chunk size has been sent - if (!chunkSizeSent) { - sendChunkSize(); - } - // cast to rtmp message - RTMPMessage rtmpMsg = (RTMPMessage) message; - IRTMPEvent msg = rtmpMsg.getBody(); - // get timestamp - int eventTime = msg.getTimestamp(); - log.debug("Message timestamp: {}", eventTime); - if (eventTime < 0) { - log.debug("Message has negative timestamp: {}", eventTime); - return; - } - // get the data type - byte dataType = msg.getDataType(); - log.trace("Data type: {}", dataType); - //create a new header for the consumer - final Header header = new Header(); - header.setTimerBase(eventTime); - //data buffer - IoBuffer buf = null; - switch (dataType) { - case Constants.TYPE_AGGREGATE: - log.trace("Aggregate data"); - data.write(msg); - break; - case Constants.TYPE_AUDIO_DATA: - log.trace("Audio data"); - buf = ((AudioData) msg).getData(); - if (buf != null) { - AudioData audioData = new AudioData(buf.asReadOnlyBuffer()); - audioData.setHeader(header); - audioData.setTimestamp(header.getTimer()); - log.trace("Source type: {}", ((AudioData) msg).getSourceType()); - audioData.setSourceType(((AudioData) msg).getSourceType()); - audio.write(audioData); - } else { - log.warn("Audio data was not found"); - } - break; - case Constants.TYPE_VIDEO_DATA: - log.trace("Video data"); - buf = ((VideoData) msg).getData(); - if (buf != null) { - VideoData videoData = new VideoData(buf.asReadOnlyBuffer()); - videoData.setHeader(header); - videoData.setTimestamp(header.getTimer()); - log.trace("Source type: {}", ((VideoData) msg).getSourceType()); - videoData.setSourceType(((VideoData) msg).getSourceType()); - video.write(videoData); - } else { - log.warn("Video data was not found"); - } - break; - case Constants.TYPE_PING: - log.trace("Ping"); - Ping ping = new Ping((Ping) msg); - ping.setHeader(header); - conn.ping(ping); - break; - case Constants.TYPE_STREAM_METADATA: - log.trace("Meta data"); - Notify notify = new Notify(((Notify) msg).getData().asReadOnlyBuffer()); - notify.setHeader(header); - notify.setTimestamp(header.getTimer()); - data.write(notify); - break; - case Constants.TYPE_FLEX_STREAM_SEND: - log.trace("Flex stream send"); - // TODO: okay to send this also to AMF0 clients? - FlexStreamSend send = new FlexStreamSend(((Notify) msg).getData().asReadOnlyBuffer()); - send.setHeader(header); - send.setTimestamp(header.getTimer()); - data.write(send); - break; - case Constants.TYPE_BYTES_READ: - log.trace("Bytes read"); - BytesRead bytesRead = new BytesRead(((BytesRead) msg).getBytesRead()); - bytesRead.setHeader(header); - bytesRead.setTimestamp(header.getTimer()); - conn.getChannel((byte) 2).write(bytesRead); - break; - default: - log.trace("Default: {}", dataType); - data.write(msg); - } - } else { - log.debug("Unhandled push message: {}", message); - if (log.isTraceEnabled()) { - Class clazz = message.getClass(); - log.trace("Class info - name: {} declaring: {} enclosing: {}", new Object[] { clazz.getName(), clazz.getDeclaringClass(), clazz.getEnclosingClass() }); - } - } - } - - /** {@inheritDoc} */ - public void onPipeConnectionEvent(PipeConnectionEvent event) { - switch (event.getType()) { - case PipeConnectionEvent.PROVIDER_DISCONNECT: - // XXX should put the channel release code in ConsumerService - closeChannels(); - break; - default: - } - } - - /** {@inheritDoc} */ - public void onOOBControlMessage(IMessageComponent source, IPipe pipe, OOBControlMessage oobCtrlMsg) { - if ("ConnectionConsumer".equals(oobCtrlMsg.getTarget())) { - String serviceName = oobCtrlMsg.getServiceName(); - log.trace("Service name: {}", serviceName); - if ("pendingCount".equals(serviceName)) { - oobCtrlMsg.setResult(conn.getPendingMessages()); - } else if ("pendingVideoCount".equals(serviceName)) { - IClientStream stream = conn.getStreamByChannelId(video.getId()); - if (stream != null) { - oobCtrlMsg.setResult(conn.getPendingVideoMessages(stream.getStreamId())); - } else { - oobCtrlMsg.setResult(0L); - } - } else if ("writeDelta".equals(serviceName)) { - //TODO: Revisit the max stream value later - long maxStream = 120 * 1024; - // Return the current delta between sent bytes and bytes the client - // reported to have received, and the interval the client should use - // for generating BytesRead messages (half of the allowed bandwidth). - oobCtrlMsg.setResult(new Long[] { conn.getWrittenBytes() - conn.getClientBytesRead(), maxStream / 2 }); - } else if ("chunkSize".equals(serviceName)) { - int newSize = (Integer) oobCtrlMsg.getServiceParamMap().get("chunkSize"); - if (newSize != chunkSize) { - chunkSize = newSize; - sendChunkSize(); - } - } - } - } - - /** - * Send the chunk size - */ - private void sendChunkSize() { - log.debug("Sending chunk size: {}", chunkSize); - ChunkSize chunkSizeMsg = new ChunkSize(chunkSize); - conn.getChannel((byte) 2).write(chunkSizeMsg); - chunkSizeSent = true; - } - - /** - * Close all the channels - */ - private void closeChannels() { - conn.closeChannel(video.getId()); - conn.closeChannel(audio.getId()); - conn.closeChannel(data.getId()); - } - -} diff --git a/src/main/java/org/red5/server/stream/consumer/FileConsumer.java b/src/main/java/org/red5/server/stream/consumer/FileConsumer.java deleted file mode 100644 index 3a33ee397..000000000 --- a/src/main/java/org/red5/server/stream/consumer/FileConsumer.java +++ /dev/null @@ -1,900 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream.consumer; - -import java.io.File; -import java.io.IOException; -import java.nio.channels.ClosedChannelException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.PriorityQueue; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import org.apache.mina.core.buffer.IoBuffer; -import org.red5.io.IStreamableFile; -import org.red5.io.ITag; -import org.red5.io.ITagWriter; -import org.red5.io.flv.impl.Tag; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.service.IStreamableFileService; -import org.red5.server.api.stream.IClientStream; -import org.red5.server.api.stream.IStreamableFileFactory; -import org.red5.server.messaging.IMessage; -import org.red5.server.messaging.IMessageComponent; -import org.red5.server.messaging.IPipe; -import org.red5.server.messaging.IPipeConnectionListener; -import org.red5.server.messaging.IPushableConsumer; -import org.red5.server.messaging.OOBControlMessage; -import org.red5.server.messaging.PipeConnectionEvent; -import org.red5.server.net.rtmp.event.FlexStreamSend; -import org.red5.server.net.rtmp.event.IRTMPEvent; -import org.red5.server.net.rtmp.event.VideoData; -import org.red5.server.net.rtmp.event.VideoData.FrameType; -import org.red5.server.net.rtmp.message.Constants; -import org.red5.server.stream.IStreamData; -import org.red5.server.stream.StreamableFileFactory; -import org.red5.server.stream.message.RTMPMessage; -import org.red5.server.stream.message.ResetMessage; -import org.red5.server.util.ScopeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.scheduling.concurrent.CustomizableThreadFactory; - -/** - * Consumer that pushes messages to file. Used when recording live streams. - * - * @author The Red5 Project - * @author Paul Gregoire (mondain@gmail.com) - * @author Vladimir Hmelyoff (vlhm@splitmedialabs.com) - * @author Octavian Naicu (naicuoctavian@gmail.com) - */ -public class FileConsumer implements Constants, IPushableConsumer, IPipeConnectionListener { - - private static final Logger log = LoggerFactory.getLogger(FileConsumer.class); - - /** - * Executor for all writer jobs - */ - private static ScheduledExecutorService scheduledExecutorService; - - /** - * Queue writer thread count - */ - private int schedulerThreadSize = 1; - - /** - * Queue to hold data for delayed writing - */ - private PriorityQueue queue; - - /** - * Reentrant lock - */ - private ReentrantReadWriteLock reentrantLock; - - /** - * Write lock - */ - private volatile Lock writeLock; - - /** - * Read lock - */ - private volatile Lock readLock; - - /** - * Scope - */ - private IScope scope; - - /** - * File - */ - private File file; - - /** - * Tag writer - */ - private ITagWriter writer; - - /** - * Operation mode - */ - private String mode; - - /** - * Start timestamp - */ - private int startTimestamp = -1; - - /** - * Last write timestamp - */ - @SuppressWarnings("unused") - private int lastTimestamp; - - /** - * Video decoder configuration - */ - private ITag videoConfigurationTag; - - /** - * Audio decoder configuration - */ - private ITag audioConfigurationTag; - - /** - * Number of queued items needed before writes are initiated - */ - private int queueThreshold = -1; - - /** - * Percentage of the queue which is sliced for writing - */ - private int percentage = 25; - - /** - * Whether or not to use a queue for delaying file writes. The queue is - * useful for keeping Tag items in their expected order based on their time - * stamp. - */ - private boolean delayWrite = false; - - /** - * Tracks the last timestamp written to prevent backwards time stamped data. - */ - private volatile int lastWrittenTs = -1; - - /** - * Keeps track of the last spawned write worker. - */ - private volatile Future writerFuture; - - private volatile boolean gotVideoKeyFrame; - - /** - * Default ctor - */ - public FileConsumer() { - if (scheduledExecutorService == null) { - scheduledExecutorService = Executors.newScheduledThreadPool(schedulerThreadSize, new CustomizableThreadFactory("FileConsumerExecutor-")); - } - } - - /** - * Creates file consumer - * - * @param scope - * Scope of consumer - * @param file - * File - */ - public FileConsumer(IScope scope, File file) { - this(); - this.scope = scope; - this.file = file; - } - - /** - * Push message through pipe - * - * @param pipe - * Pipe - * @param message - * Message to push - * @throws IOException - * if message could not be written - */ - @SuppressWarnings("rawtypes") - public void pushMessage(IPipe pipe, IMessage message) throws IOException { - if (message instanceof RTMPMessage) { - final IRTMPEvent msg = ((RTMPMessage) message).getBody(); - // get the type - byte dataType = msg.getDataType(); - // get the timestamp - int timestamp = msg.getTimestamp(); - log.debug("Data type: {} timestamp: {}", dataType, timestamp); - // if we're dealing with a FlexStreamSend IRTMPEvent, this avoids - // relative timestamp calculations - if (!(msg instanceof FlexStreamSend)) { - log.trace("Not FlexStreamSend type"); - lastTimestamp = timestamp; - } - // ensure that our first video frame written is a key frame - if (msg instanceof VideoData) { - if (!gotVideoKeyFrame) { - VideoData video = (VideoData) msg; - if (video.getFrameType() == FrameType.KEYFRAME) { - log.debug("Got our first keyframe"); - gotVideoKeyFrame = true; - } else { - // skip this frame bail out - log.debug("Skipping video data since keyframe has not been written yet"); - return; - } - } - } - // initialize a writer - if (writer == null) { - init(); - } - // if writes are delayed, queue the data and sort it by time - if (!delayWrite) { - write(timestamp, msg); - } else { - QueuedData queued = null; - if (msg instanceof IStreamData) { - if (log.isTraceEnabled()) { - log.trace("Stream data, body saved. Data type: {} class type: {}", dataType, msg.getClass().getName()); - } - try { - queued = new QueuedData(timestamp, dataType, ((IStreamData) msg).duplicate()); - } catch (ClassNotFoundException e) { - log.warn("Exception queueing stream data", e); - } - } else { - // XXX what type of message are we saving that has no body data?? - if (log.isTraceEnabled()) { - log.trace("Non-stream data, body not saved. Data type: {} class type: {}", dataType, msg.getClass().getName()); - } - queued = new QueuedData(timestamp, dataType); - } - writeLock.lock(); - try { - // add to the queue - queue.add(queued); - } finally { - writeLock.unlock(); - } - int queueSize = 0; - readLock.lock(); - try { - queueSize = queue.size(); - } finally { - readLock.unlock(); - } - if (msg instanceof VideoData) { - writeQueuedDataSlice(createTimestampLimitedSlice(msg.getTimestamp())); - } else if (queueThreshold >= 0 && queueSize >= queueThreshold) { - writeQueuedDataSlice(createFixedLengthSlice(queueThreshold / (100 / percentage))); - } - } - } else if (message instanceof ResetMessage) { - startTimestamp = -1; - } - } - - private void writeQueuedDataSlice(final QueuedData[] slice) { - if (acquireWriteFuture(slice.length)) { - // spawn a writer - writerFuture = scheduledExecutorService.submit(new Runnable() { - public void run() { - log.trace("Spawning queue writer thread"); - doWrites(slice); - } - }); - } else { - // since we failed to write, put the sliced data back into the queue - writeLock.lock(); - try { - List unwritten = Arrays.asList(slice); - for (QueuedData queued : unwritten) { - if (queued.hasData()) { - queue.add(queued); - } - } - } finally { - writeLock.unlock(); - } - } - } - - private QueuedData[] createFixedLengthSlice(int sliceLength) { - log.debug("Creating data slice to write of length {}.", sliceLength); - // get the slice - final QueuedData[] slice = new QueuedData[sliceLength]; - log.trace("Slice length: {}", slice.length); - writeLock.lock(); - try { - // sort the queue - if (log.isTraceEnabled()) { - log.trace("Queue length: {}", queue.size()); - } - for (int q = 0; q < sliceLength; q++) { - slice[q] = queue.remove(); - } - if (log.isTraceEnabled()) { - log.trace("Queue length (after removal): {}", queue.size()); - } - } finally { - writeLock.unlock(); - } - return slice; - } - - private QueuedData[] createTimestampLimitedSlice(int timestamp) { - log.debug("Creating data slice up until timestamp {}.", timestamp); - // get the slice - final ArrayList slice = new ArrayList(); - writeLock.lock(); - try { - // sort the queue - if (log.isTraceEnabled()) { - log.trace("Queue length: {}", queue.size()); - } - if (!queue.isEmpty()) { - while (!queue.isEmpty() && queue.peek().getTimestamp() <= timestamp) { - slice.add(queue.remove()); - } - if (log.isTraceEnabled()) { - log.trace("Queue length (after removal): {}", queue.size()); - } - } - } finally { - writeLock.unlock(); - } - return slice.toArray(new QueuedData[slice.size()]); - } - - /** - * Get the WriteFuture with a timeout based on the length of the slice to - * write. - * - * @param sliceLength - * @return true if successful and false otherwise - */ - private boolean acquireWriteFuture(int sliceLength) { - if (sliceLength > 0) { - Object writeResult = null; - // determine a good timeout value based on the slice length to write - int timeout = sliceLength * 500; - // check for existing future - if (writerFuture != null) { - try { - // wait for a result from the last writer - writeResult = writerFuture.get(timeout, TimeUnit.MILLISECONDS); - } catch (Exception e) { - log.warn("Exception waiting for write result. Timeout: {}ms", timeout, e); - return false; - } - } - log.debug("Write future result (expect null): {}", writeResult); - return true; - } - return false; - } - - /** - * Out-of-band control message handler - * - * @param source - * Source of message - * @param pipe - * Pipe that is used to transmit OOB message - * @param oobCtrlMsg - * OOB control message - */ - public void onOOBControlMessage(IMessageComponent source, IPipe pipe, OOBControlMessage oobCtrlMsg) { - } - - /** - * Pipe connection event handler - * - * @param event - * Pipe connection event - */ - public void onPipeConnectionEvent(PipeConnectionEvent event) { - switch (event.getType()) { - case PipeConnectionEvent.CONSUMER_CONNECT_PUSH: - if (event.getConsumer() == this) { - Map paramMap = event.getParamMap(); - if (paramMap != null) { - mode = (String) paramMap.get("mode"); - } - } - break; - case PipeConnectionEvent.CONSUMER_DISCONNECT: - if (event.getConsumer() != this) { - break; - } - case PipeConnectionEvent.PROVIDER_DISCONNECT: - // we only support one provider at a time so releasing when provider - // disconnects - // uninit(); - break; - default: - break; - } - } - - /** - * Initialization - * - * @throws IOException - * I/O exception - */ - private void init() throws IOException { - log.debug("Init"); - // if the "file" is null, the consumer has been uninitialized - if (file != null) { - // if we plan to use a queue, create one - if (delayWrite) { - queue = new PriorityQueue(queueThreshold <= 0 ? 11 : queueThreshold); - // add associated locks - reentrantLock = new ReentrantReadWriteLock(); - writeLock = reentrantLock.writeLock(); - readLock = reentrantLock.readLock(); - } - IStreamableFileFactory factory = (IStreamableFileFactory) ScopeUtils.getScopeService(scope, IStreamableFileFactory.class, StreamableFileFactory.class); - File folder = file.getParentFile(); - if (!folder.exists()) { - if (!folder.mkdirs()) { - throw new IOException("Could not create parent folder"); - } - } - if (!file.isFile()) { - // Maybe the (previously existing) file has been deleted - file.createNewFile(); - } else if (!file.canWrite()) { - throw new IOException("The file is read-only"); - } - IStreamableFileService service = factory.getService(file); - IStreamableFile flv = service.getStreamableFile(file); - if (mode == null || mode.equals(IClientStream.MODE_RECORD)) { - writer = flv.getWriter(); - // write the decoder config tag if it exists - if (videoConfigurationTag != null) { - writer.writeTag(videoConfigurationTag); - videoConfigurationTag = null; - } - if (audioConfigurationTag != null) { - writer.writeTag(audioConfigurationTag); - audioConfigurationTag = null; - } - } else if (mode.equals(IClientStream.MODE_APPEND)) { - writer = flv.getAppendWriter(); - } else { - throw new IllegalStateException(String.format("Illegal mode type: %s", mode)); - } - } else { - log.warn("Consumer is uninitialized"); - } - } - - /** - * Reset or uninitialize - */ - public void uninit() { - log.debug("Uninit"); - if (writer != null) { - if (writerFuture != null) { - try { - writerFuture.get(); - } catch (Exception e) { - log.warn("Exception waiting for write result on uninit", e); - } - if (writerFuture.cancel(false)) { - log.debug("Future completed"); - } - } - writerFuture = null; - if (delayWrite) { - // write all the queued items - doWrites(); - // clear the queue - queue.clear(); - queue = null; - } - // close the writer - writer.close(); - writer = null; - } - // clear file ref - file = null; - } - - /** - * Write all the queued items to the writer. - */ - public final void doWrites() { - QueuedData[] slice = null; - writeLock.lock(); - try { - slice = queue.toArray(new QueuedData[0]); - if (queue.removeAll(Arrays.asList(slice))) { - log.debug("Queued writes transfered, count: {}", slice.length); - } - } finally { - writeLock.unlock(); - } - // sort - Arrays.sort(slice); - // write - doWrites(slice); - } - - /** - * Write a slice of the queued items to the writer. - */ - public final void doWrites(QueuedData[] slice) { - // empty the queue - for (QueuedData queued : slice) { - int tmpTs = queued.getTimestamp(); - if (lastWrittenTs <= tmpTs) { - if (queued.hasData()) { - // write the data - write(queued); - lastWrittenTs = tmpTs; - // clear the data, because we're done with it - queued.dispose(); - } else { - if (log.isTraceEnabled()) { - log.trace("Queued data was not available"); - } - } - } else { - // clear the data, since its too old - queued.dispose(); - } - } - // clear and null-out - slice = null; - } - - /** - * Write incoming data to the file. - * - * @param timestamp - * adjusted timestamp - * @param msg - * stream data - */ - private final void write(int timestamp, IRTMPEvent msg) { - byte dataType = msg.getDataType(); - log.debug("Write - timestamp: {} type: {}", timestamp, dataType); - // if the last message was a reset or we just started, use the header - // timer - if (startTimestamp == -1) { - startTimestamp = timestamp; - timestamp = 0; - } else { - timestamp -= startTimestamp; - } - // create a tag - ITag tag = new Tag(); - tag.setDataType(dataType); - tag.setTimestamp(timestamp); - // get data bytes - IoBuffer data = ((IStreamData) msg).getData().duplicate(); - if (data != null) { - tag.setBodySize(data.limit()); - tag.setBody(data); - } - // only allow blank tags if they are of audio type - if (tag.getBodySize() > 0 || dataType == ITag.TYPE_AUDIO) { - try { - if (timestamp >= 0) { - if (!writer.writeTag(tag)) { - log.warn("Tag was not written"); - } - } else { - log.warn("Skipping message with negative timestamp."); - } - } catch (IOException e) { - log.error("Error writing tag", e); - } finally { - if (data != null) { - data.clear(); - data.free(); - } - } - } - data = null; - } - - /** - * Adjust timestamp and write to the file. - * - * @param queued - * queued data for write - */ - private final void write(QueuedData queued) { - // get timestamp - int timestamp = queued.getTimestamp(); - log.debug("Write - timestamp: {} type: {}", timestamp, queued.getDataType()); - // if the last message was a reset or we just started, use the header - // timer - if (startTimestamp == -1) { - startTimestamp = timestamp; - timestamp = 0; - } else { - timestamp -= startTimestamp; - } - // get the type - byte dataType = queued.getDataType(); - // create a tag - ITag tag = new Tag(); - tag.setDataType(dataType); - tag.setTimestamp(timestamp); - // get queued - IoBuffer data = queued.getData(); - if (data != null) { - tag.setBodySize(data.limit()); - tag.setBody(data); - } - // only allow blank tags if they are of audio type - if (tag.getBodySize() > 0 || dataType == ITag.TYPE_AUDIO) { - try { - if (timestamp >= 0) { - if (!writer.writeTag(tag)) { - log.warn("Tag was not written"); - } - } else { - log.warn("Skipping message with negative timestamp."); - } - } catch (ClosedChannelException cce) { - // the channel we tried to write to is closed, we should not try - // again on that writer - log.error("The writer is no longer able to write to the file: {} writable: {}", file.getName(), file.canWrite()); - } catch (IOException e) { - log.warn("Error writing tag", e); - if (e.getCause() instanceof ClosedChannelException) { - // the channel we tried to write to is closed, we should not - // try again on that writer - log.error("The writer is no longer able to write to the file: {} writable: {}", file.getName(), file.canWrite()); - } - } finally { - if (data != null) { - data.clear(); - data.free(); - } - } - } - data = null; - } - - /** - * Sets a video decoder configuration; some codecs require this, such as - * AVC. - * - * @param decoderConfig - * video codec configuration - */ - public void setVideoDecoderConfiguration(IRTMPEvent decoderConfig) { - videoConfigurationTag = new Tag(); - videoConfigurationTag.setDataType(decoderConfig.getDataType()); - videoConfigurationTag.setTimestamp(0); - if (decoderConfig instanceof IStreamData) { - IoBuffer data = ((IStreamData) decoderConfig).getData().asReadOnlyBuffer(); - videoConfigurationTag.setBodySize(data.limit()); - videoConfigurationTag.setBody(data); - } - } - - /** - * Sets a audio decoder configuration; some codecs require this, such as - * AAC. - * - * @param decoderConfig - * audio codec configuration - */ - public void setAudioDecoderConfiguration(IRTMPEvent decoderConfig) { - audioConfigurationTag = new Tag(); - audioConfigurationTag.setDataType(decoderConfig.getDataType()); - audioConfigurationTag.setTimestamp(0); - if (decoderConfig instanceof IStreamData) { - IoBuffer data = ((IStreamData) decoderConfig).getData().asReadOnlyBuffer(); - audioConfigurationTag.setBodySize(data.limit()); - audioConfigurationTag.setBody(data); - } - } - - /** - * Sets the scope for this consumer. - * - * @param scope - */ - public void setScope(IScope scope) { - this.scope = scope; - } - - /** - * Sets the file we're writing to. - * - * @param file - */ - public void setFile(File file) { - this.file = file; - } - - /** - * Returns the file. - * - * @return file - */ - public File getFile() { - return file; - } - - /** - * Sets the threshold for the queue. When the threshold is met a worker is - * spawned to empty the sorted queue to the writer. - * - * @param queueThreshold - * number of items to queue before spawning worker - */ - public void setQueueThreshold(int queueThreshold) { - this.queueThreshold = queueThreshold; - } - - /** - * Returns the size of the delayed writing queue. - * - * @return queue length - */ - public int getQueueThreshold() { - return queueThreshold; - } - - /** - * Whether or not the queue should be utilized. - * - * @return true if using the queue, false if sending directly to the writer - */ - public boolean isDelayWrite() { - return delayWrite; - } - - /** - * Sets whether or not to use the queue. - * - * @param delayWrite - * true to use the queue, false if not - */ - public void setDelayWrite(boolean delayWrite) { - this.delayWrite = delayWrite; - } - - /** - * @return the schedulerThreadSize - */ - public int getSchedulerThreadSize() { - return schedulerThreadSize; - } - - /** - * @param schedulerThreadSize - * the schedulerThreadSize to set - */ - public void setSchedulerThreadSize(int schedulerThreadSize) { - this.schedulerThreadSize = schedulerThreadSize; - } - - /** - * Sets the recording mode. - * - * @param mode - * either "record" or "append" depending on the type of action to - * perform - */ - public void setMode(String mode) { - this.mode = mode; - } - - /** - * Queued data wrapper. - */ - private final static class QueuedData implements Comparable { - - final int timestamp; - - final byte dataType; - - boolean disposed; - - @SuppressWarnings("rawtypes") - final IStreamData streamData; - - QueuedData(int timestamp, byte dataType) { - this.timestamp = timestamp; - this.dataType = dataType; - this.streamData = null; - } - - @SuppressWarnings("rawtypes") - QueuedData(int timestamp, byte dataType, IStreamData streamData) { - this.timestamp = timestamp; - this.dataType = dataType; - this.streamData = streamData; - } - - public int getTimestamp() { - return timestamp; - } - - public byte getDataType() { - return dataType; - } - - public IoBuffer getData() { - return streamData.getData().asReadOnlyBuffer(); - } - - public boolean hasData() { - return disposed; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + dataType; - result = prime * result + timestamp; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - QueuedData other = (QueuedData) obj; - if (dataType != other.dataType) { - return false; - } - if (timestamp != other.timestamp) { - return false; - } - return true; - } - - @Override - public int compareTo(QueuedData other) { - if (timestamp > other.timestamp) { - return 1; - } else if (timestamp < other.timestamp) { - return -1; - } - return 0; - } - - public void dispose() { - try { - final IoBuffer data = streamData.getData(); - data.free(); - data.buf().clear(); - } catch (Exception e) { - } finally { - disposed = true; - } - } - - } - -} diff --git a/src/main/java/org/red5/server/stream/message/RTMPMessage.java b/src/main/java/org/red5/server/stream/message/RTMPMessage.java deleted file mode 100644 index 5a9311aea..000000000 --- a/src/main/java/org/red5/server/stream/message/RTMPMessage.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream.message; - -import org.red5.server.messaging.AbstractMessage; -import org.red5.server.net.rtmp.RTMPType; -import org.red5.server.net.rtmp.event.IRTMPEvent; - -/** - * RTMP message - */ -public class RTMPMessage extends AbstractMessage { - - private final IRTMPEvent body; - - /** - * Creates a new rtmp message. - * - * @param body value to set for property 'body' - */ - private RTMPMessage(IRTMPEvent body) { - this.body = body; - this.setMessageType(RTMPType.valueOf(body.getDataType())); - } - - /** - * Creates a new rtmp message. - * - * @param body value to set for property 'body' - * @param eventTime updated timestamp - */ - private RTMPMessage(IRTMPEvent body, int eventTime) { - this.body = body; - this.body.setTimestamp(eventTime); - this.setMessageType(RTMPType.valueOf(body.getDataType())); - } - - /** - * Return RTMP message body - * - * @return Value for property 'body'. - */ - public IRTMPEvent getBody() { - return body; - } - - /** - * Builder for RTMPMessage. - * - * @param body event data - * @return Immutable RTMPMessage - */ - public final static RTMPMessage build(IRTMPEvent body) { - return new RTMPMessage(body); - } - - /** - * Builder for RTMPMessage. - * - * @param body event data - * @param eventTime time value to set on the event body - * @return Immutable RTMPMessage - */ - public final static RTMPMessage build(IRTMPEvent body, int eventTime) { - return new RTMPMessage(body, eventTime); - } - - /** - * Builder for RTMPMessage. - * - * @param body event data - * @param sourceType live or vod - * @return Immutable RTMPMessage - */ - public final static RTMPMessage build(IRTMPEvent body, byte sourceType) { - RTMPMessage msg = new RTMPMessage(body); - msg.body.setSourceType(sourceType); - return msg; - } - -} diff --git a/src/main/java/org/red5/server/stream/message/ResetMessage.java b/src/main/java/org/red5/server/stream/message/ResetMessage.java deleted file mode 100644 index 40c7b093e..000000000 --- a/src/main/java/org/red5/server/stream/message/ResetMessage.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream.message; - -import org.red5.server.messaging.AbstractMessage; - -/** - * To notify the client to reset the playing state. - * - * @author The Red5 Project - * @author Steven Gong (steven.gong@gmail.com) - */ -public class ResetMessage extends AbstractMessage { - - { - this.messageType = "reset"; - } - -} diff --git a/src/main/java/org/red5/server/stream/message/StatusMessage.java b/src/main/java/org/red5/server/stream/message/StatusMessage.java deleted file mode 100644 index 62624f5cc..000000000 --- a/src/main/java/org/red5/server/stream/message/StatusMessage.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.stream.message; - -import org.red5.server.messaging.AbstractMessage; -import org.red5.server.net.rtmp.status.Status; - -public class StatusMessage extends AbstractMessage { - - private Status body; - - { - this.messageType = "status"; - } - - /** - * Getter for property 'body'. - * - * @return Value for property 'body'. - */ - public Status getBody() { - return body; - } - - /** - * Setter for property 'body'. - * - * @param body Value to set for property 'body'. - */ - public void setBody(Status body) { - this.body = body; - } - -} diff --git a/src/main/java/org/red5/server/util/FileUtil.java b/src/main/java/org/red5/server/util/FileUtil.java deleted file mode 100644 index 901c2361a..000000000 --- a/src/main/java/org/red5/server/util/FileUtil.java +++ /dev/null @@ -1,568 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.util; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Enumeration; -import java.util.Random; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Generic file utility containing useful file or directory - * manipulation functions. - * - * @author Paul Gregoire (mondain@gmail.com) - * @author Dominick Accattato (daccattato@gmail.com) - */ -public class FileUtil { - - private static Logger log = LoggerFactory.getLogger(FileUtil.class); - - public static void copyFile(File source, File dest) throws IOException { - log.debug("Copy from {} to {}", source.getAbsoluteFile(), dest.getAbsoluteFile()); - FileInputStream fi = new FileInputStream(source); - FileChannel fic = fi.getChannel(); - MappedByteBuffer mbuf = fic.map(FileChannel.MapMode.READ_ONLY, 0, source.length()); - fic.close(); - fi.close(); - fi = null; - - // ensure the destination directory exists - if (!dest.exists()) { - String destPath = dest.getPath(); - log.debug("Destination path: {}", destPath); - String destDir = destPath.substring(0, destPath.lastIndexOf(File.separatorChar)); - log.debug("Destination dir: {}", destDir); - File dir = new File(destDir); - if (!dir.exists()) { - if (dir.mkdirs()) { - log.debug("Directory created"); - } else { - log.warn("Directory not created"); - } - } - dir = null; - } - - FileOutputStream fo = new FileOutputStream(dest); - FileChannel foc = fo.getChannel(); - foc.write(mbuf); - foc.close(); - fo.close(); - fo = null; - - mbuf.clear(); - mbuf = null; - } - - public static void copyFile(String source, String dest) throws IOException { - copyFile(new File(source), new File(dest)); - } - - public static void moveFile(String source, String dest) throws IOException { - copyFile(source, dest); - File src = new File(source); - if (src.exists() && src.canRead()) { - if (src.delete()) { - log.debug("Source file was deleted"); - } else { - log.debug("Source file was not deleted, the file will be deleted on exit"); - src.deleteOnExit(); - } - } else { - log.warn("Source file could not be accessed for removal"); - } - src = null; - } - - /** - * Deletes a directory and its contents. This will fail if there are any - * file locks or if the directory cannot be emptied. - * - * @param directory directory to delete - * @throws IOException if directory cannot be deleted - * @return true if directory was successfully deleted; false if directory - * did not exist - */ - public static boolean deleteDirectory(String directory) throws IOException { - return deleteDirectory(directory, false); - } - - /** - * Deletes a directory and its contents. This will fail if there are any - * file locks or if the directory cannot be emptied. - * - * @param directory directory to delete - * @param useOSNativeDelete flag to signify use of operating system delete function - * @throws IOException if directory cannot be deleted - * @return true if directory was successfully deleted; false if directory - * did not exist - */ - public static boolean deleteDirectory(String directory, boolean useOSNativeDelete) throws IOException { - boolean result = false; - if (!useOSNativeDelete) { - File dir = new File(directory); - // first all files have to be cleared out - for (File file : dir.listFiles()) { - if (file.delete()) { - log.debug("{} was deleted", file.getName()); - } else { - log.debug("{} was not deleted", file.getName()); - file.deleteOnExit(); - } - file = null; - } - // not you may remove the dir - if (dir.delete()) { - log.debug("Directory was deleted"); - result = true; - } else { - log.debug("Directory was not deleted, it may be deleted on exit"); - dir.deleteOnExit(); - } - dir = null; - } else { - Process p = null; - Thread std = null; - try { - Runtime runTime = Runtime.getRuntime(); - log.debug("Execute runtime"); - //determine file system type - if (File.separatorChar == '\\') { - //we are windows - p = runTime.exec("CMD /D /C \"RMDIR /Q /S " + directory.replace('/', '\\') + "\""); - } else { - //we are unix variant - p = runTime.exec("rm -rf " + directory.replace('\\', File.separatorChar)); - } - // observe std out - std = stdOut(p); - // wait for the observer threads to finish - while (std.isAlive()) { - try { - Thread.sleep(250); - } catch (Exception e) { - } - } - log.debug("Process threads wait exited"); - result = true; - } catch (Exception e) { - log.error("Error running delete script", e); - } finally { - if (null != p) { - log.debug("Destroying process"); - p.destroy(); - p = null; - } - std = null; - } - } - return result; - } - - /** - * Rename a file natively; using REN on Windows and mv on *nix. - * - * @param from old name - * @param to new name - */ - public static void rename(String from, String to) { - Process p = null; - Thread std = null; - try { - Runtime runTime = Runtime.getRuntime(); - log.debug("Execute runtime"); - //determine file system type - if (File.separatorChar == '\\') { - //we are windows - p = runTime.exec("CMD /D /C \"REN " + from + ' ' + to + "\""); - } else { - //we are unix variant - p = runTime.exec("mv -f " + from + ' ' + to); - } - // observe std out - std = stdOut(p); - // wait for the observer threads to finish - while (std.isAlive()) { - try { - Thread.sleep(250); - } catch (Exception e) { - } - } - log.debug("Process threads wait exited"); - } catch (Exception e) { - log.error("Error running delete script", e); - } finally { - if (null != p) { - log.debug("Destroying process"); - p.destroy(); - p = null; - std = null; - } - } - } - - /** - * Special method for capture of StdOut. - * - * @return stdOut thread - */ - private final static Thread stdOut(final Process p) { - final byte[] empty = new byte[128]; - for (int b = 0; b < empty.length; b++) { - empty[b] = (byte) 0; - } - Thread std = new Thread() { - public void run() { - StringBuilder sb = new StringBuilder(1024); - byte[] buf = new byte[128]; - BufferedInputStream bis = new BufferedInputStream(p.getInputStream()); - log.debug("Process output:"); - try { - while (bis.read(buf) != -1) { - sb.append(new String(buf).trim()); - // clear buffer - System.arraycopy(empty, 0, buf, 0, buf.length); - } - log.debug(sb.toString()); - bis.close(); - } catch (Exception e) { - log.error("{}", e); - } - } - }; - std.setDaemon(true); - std.start(); - return std; - } - - /** - * Create a directory. - * - * @param directory directory to make - * @return whether a new directory was made - * @throws IOException if directory does not already exist or cannot be made - */ - public static boolean makeDirectory(String directory) throws IOException { - return makeDirectory(directory, false); - } - - /** - * Create a directory. The parent directories will be created if - * createParents is passed as true. - * - * @param directory directory - * @param createParents whether to create all parents - * @return true if directory was created; false if it already existed - * @throws IOException if we cannot create directory - * - */ - public static boolean makeDirectory(String directory, boolean createParents) throws IOException { - boolean created = false; - File dir = new File(directory); - if (createParents) { - created = dir.mkdirs(); - if (created) { - log.debug("Directory created: {}", dir.getAbsolutePath()); - } else { - log.debug("Directory was not created: {}", dir.getAbsolutePath()); - } - } else { - created = dir.mkdir(); - if (created) { - log.debug("Directory created: {}", dir.getAbsolutePath()); - } else { - log.debug("Directory was not created: {}", dir.getAbsolutePath()); - } - } - dir = null; - return created; - } - - /** - * Unzips a war file to an application located under the webapps directory - * - * @param compressedFileName The String name of the war file - * @param destinationDir The destination directory, ie: webapps - */ - public static void unzip(String compressedFileName, String destinationDir) { - - //strip everything except the applications name - String dirName = null; - - // checks to see if there is a dash "-" in the filename of the war. - String applicationName = compressedFileName.substring(compressedFileName.lastIndexOf("/")); - - int dashIndex = applicationName.indexOf('-'); - if (dashIndex != -1) { - //strip everything except the applications name - dirName = compressedFileName.substring(0, dashIndex); - } else { - //grab every char up to the last '.' - dirName = compressedFileName.substring(0, compressedFileName.lastIndexOf('.')); - } - - log.debug("Directory: {}", dirName); - //String tmpDir = System.getProperty("java.io.tmpdir"); - File zipDir = new File(compressedFileName); - File parent = zipDir.getParentFile(); - log.debug("Parent: {}", (parent != null ? parent.getName() : null)); - //File tmpDir = new File(System.getProperty("java.io.tmpdir"), dirName); - File tmpDir = new File(destinationDir); - - // make the war directory - log.debug("Making directory: {}", tmpDir.mkdirs()); - ZipFile zf = null; - try { - zf = new ZipFile(compressedFileName); - Enumeration e = zf.entries(); - while (e.hasMoreElements()) { - ZipEntry ze = (ZipEntry) e.nextElement(); - log.debug("Unzipping {}", ze.getName()); - if (ze.isDirectory()) { - log.debug("is a directory"); - File dir = new File(tmpDir + "/" + ze.getName()); - Boolean tmp = dir.mkdir(); - log.debug("{}", tmp); - continue; - } - - // checks to see if a zipEntry contains a path - // i.e. ze.getName() == "META-INF/MANIFEST.MF" - // if this case is true, then we create the path first - if (ze.getName().lastIndexOf("/") != -1) { - String zipName = ze.getName(); - String zipDirStructure = zipName.substring(0, zipName.lastIndexOf("/")); - File completeDirectory = new File(tmpDir + "/" + zipDirStructure); - if (!completeDirectory.exists()) { - if (!completeDirectory.mkdirs()) { - log.error("could not create complete directory structure"); - } - } - } - - // creates the file - FileOutputStream fout = new FileOutputStream(tmpDir + "/" + ze.getName()); - InputStream in = zf.getInputStream(ze); - copy(in, fout); - in.close(); - fout.close(); - } - e = null; - } catch (IOException e) { - log.error("Errored unzipping", e); - //log.warn("Exception {}", e); - } finally { - if (zf != null) { - try { - zf.close(); - } catch (IOException e) { - } - } - } - } - - public static void copy(InputStream in, OutputStream out) throws IOException { - synchronized (in) { - synchronized (out) { - byte[] buffer = new byte[256]; - while (true) { - int bytesRead = in.read(buffer); - if (bytesRead == -1) - break; - out.write(buffer, 0, bytesRead); - } - } - } - } - - /** - * Unzips a given archive to a specified destination directory. - * - * @param compressedFileName - * @param destinationDir - */ - // public static void unzip(String compressedFileName, String destinationDir) { - // log.debug("Unzip - file: {} destination: {}", compressedFileName, destinationDir); - // try { - // final int BUFFER = 2048; - // BufferedOutputStream dest = null; - // FileInputStream fis = new FileInputStream(compressedFileName); - // CheckedInputStream checksum = new CheckedInputStream(fis, - // new Adler32()); - // ZipInputStream zis = new ZipInputStream(new BufferedInputStream( - // checksum)); - // ZipEntry entry; - // while ((entry = zis.getNextEntry()) != null) { - // log.debug("Extracting: {}", entry); - // String name = entry.getName(); - // int count; - // byte data[] = new byte[BUFFER]; - // // write the files to the disk - // File destFile = new File(destinationDir, name); - // log.debug("Absolute path: {}", destFile.getAbsolutePath()); - // //create dirs as needed, look for file extension to determine type - // if (entry.isDirectory()) { - // log.debug("Entry is detected as a directory"); - // if (destFile.mkdirs()) { - // log.debug("Directory created: {}", destFile.getName()); - // } else { - // log.warn("Directory was not created: {}", destFile.getName()); - // } - // destFile = null; - // continue; - // } - // - // FileOutputStream fos = new FileOutputStream(destFile); - // dest = new BufferedOutputStream(fos, BUFFER); - // while ((count = zis.read(data, 0, BUFFER)) != -1) { - // dest.write(data, 0, count); - // } - // dest.flush(); - // dest.close(); - // destFile = null; - // } - // zis.close(); - // log.debug("Checksum: {}", checksum.getChecksum().getValue()); - // } catch (Exception e) { - // log.error("Error unzipping {}", compressedFileName, e); - // log.warn("Exception {}", e); - // } - // - // } - - /** - * Quick-n-dirty directory formatting to support launching in windows, specifically from ant. - * @param absWebappsPath abs webapps path - * @param contextDirName conext directory name - * @return full path - */ - public static String formatPath(String absWebappsPath, String contextDirName) { - StringBuilder path = new StringBuilder(absWebappsPath.length() + contextDirName.length()); - path.append(absWebappsPath); - if (log.isTraceEnabled()) { - log.trace("Path start: {}", path.toString()); - } - int idx = -1; - if (File.separatorChar != '/') { - while ((idx = path.indexOf(File.separator)) != -1) { - path.deleteCharAt(idx); - path.insert(idx, '/'); - } - } - if (log.isTraceEnabled()) { - log.trace("Path step 1: {}", path.toString()); - } - //remove any './' - if ((idx = path.indexOf("./")) != -1) { - path.delete(idx, idx + 2); - } - if (log.isTraceEnabled()) { - log.trace("Path step 2: {}", path.toString()); - } - //add / to base path if one doesnt exist - if (path.charAt(path.length() - 1) != '/') { - path.append('/'); - } - if (log.isTraceEnabled()) { - log.trace("Path step 3: {}", path.toString()); - } - //remove the / from the beginning of the context dir - if (contextDirName.charAt(0) == '/' && path.charAt(path.length() - 1) == '/') { - path.append(contextDirName.substring(1)); - } else { - path.append(contextDirName); - } - if (log.isTraceEnabled()) { - log.trace("Path step 4: {}", path.toString()); - } - return path.toString(); - } - - /** - * Generates a custom name containing numbers and an underscore ex. 282818_00023. - * The name contains current seconds and a random number component. - * - * @return custom name - */ - public static String generateCustomName() { - Random random = new Random(); - StringBuilder sb = new StringBuilder(); - sb.append(PropertyConverter.getCurrentTimeSeconds()); - sb.append('_'); - int i = random.nextInt(99999); - if (i < 10) { - sb.append("0000"); - } else if (i < 100) { - sb.append("000"); - } else if (i < 1000) { - sb.append("00"); - } else if (i < 10000) { - sb.append('0'); - } - sb.append(i); - return sb.toString(); - } - - /** - * Reads all the bytes of a given file into an array. If the file size exceeds Integer.MAX_VALUE, it will - * be truncated. - * - * @param localSwfFile - * @return file bytes - */ - public static byte[] readAsByteArray(File localSwfFile) { - byte[] fileBytes = new byte[(int) localSwfFile.length()]; - byte[] b = new byte[1]; - FileInputStream fis = null; - try { - fis = new FileInputStream(localSwfFile); - for (int i = 0; i < Integer.MAX_VALUE; i++) { - if (fis.read(b) != -1) { - fileBytes[i] = b[0]; - } else { - break; - } - } - } catch (IOException e) { - log.warn("Exception reading file bytes", e); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - } - } - } - return fileBytes; - } - -} diff --git a/src/main/java/org/red5/server/util/HttpConnectionUtil.java b/src/main/java/org/red5/server/util/HttpConnectionUtil.java deleted file mode 100644 index 2eb4aa019..000000000 --- a/src/main/java/org/red5/server/util/HttpConnectionUtil.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.util; - -import java.io.IOException; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.ParseException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.util.EntityUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Utility for using HTTP connections. - * - * @author The Red5 Project - * @author Paul Gregoire (mondain@gmail.com) - */ -public class HttpConnectionUtil { - - private static Logger log = LoggerFactory.getLogger(HttpConnectionUtil.class); - - private static final String userAgent = "Mozilla/4.0 (compatible; Red5 Server)"; - - private static PoolingHttpClientConnectionManager connectionManager; - - private static int connectionTimeout = 7000; - - static { - // Create an HttpClient with the PoolingHttpClientConnectionManager. - // This connection manager must be used if more than one thread will - // be using the HttpClient. - connectionManager = new PoolingHttpClientConnectionManager(); - connectionManager.setMaxTotal(40); - } - - /** - * Returns a client with all our selected properties / params. - * - * @return client - */ - public static final HttpClient getClient() { - return getClient(connectionTimeout); - } - - /** - * Returns a client with all our selected properties / params. - * - * @param timeout - socket timeout to set - * @return client - */ - public static final HttpClient getClient(int timeout) { - HttpClientBuilder client = HttpClientBuilder.create(); - client.setConnectionManager(connectionManager); - // dont retry - client.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)); - // establish a connection within x seconds - RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build(); - client.setDefaultRequestConfig(config); - // no redirects - client.disableRedirectHandling(); - // set custom ua - client.setUserAgent(userAgent); - // set the proxy if the user has one set - if ((System.getProperty("http.proxyHost") != null) && (System.getProperty("http.proxyPort") != null)) { - HttpHost proxy = new HttpHost(System.getProperty("http.proxyHost").toString(), Integer.valueOf(System.getProperty("http.proxyPort"))); - client.setProxy(proxy); - } - - return client.build(); - } - - /** - * Logs details about the request error. - * - * @param response - * @throws IOException - * @throws ParseException - */ - public static void handleError(HttpResponse response) throws ParseException, IOException { - log.debug("{}", response.getStatusLine().toString()); - HttpEntity entity = response.getEntity(); - if (entity != null) { - log.debug("{}", EntityUtils.toString(entity)); - } - } - - /** - * @return the connectionTimeout - */ - public int getConnectionTimeout() { - return connectionTimeout; - } - - /** - * @param connectionTimeout the connectionTimeout to set - */ - public void setConnectionTimeout(int connectionTimeout) { - HttpConnectionUtil.connectionTimeout = connectionTimeout; - } - -} diff --git a/src/main/java/org/red5/server/util/PropertyConverter.java b/src/main/java/org/red5/server/util/PropertyConverter.java deleted file mode 100644 index 919141d71..000000000 --- a/src/main/java/org/red5/server/util/PropertyConverter.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.util; - -import java.util.Calendar; - -import org.apache.commons.lang3.StringUtils; - -/** - * Converter for properties originating from properties files. Predetermined - * string formats are converted into other usable types such as timestamps. - * - * @author Paul Gregoire (mondain@gmail.com) - */ -public class PropertyConverter { - - /** - * Converts a string denoting an amount of time into milliseconds and adds - * it to the current date. Strings are expected to follow this form where # - * equals a digit: #M The following are permitted for denoting time: H = - * hours, M = minutes, S = seconds - * - * @param time time - * @return time in milliseconds - */ - public static long convertStringToFutureTimeMillis(String time) { - Calendar exp = Calendar.getInstance(); - if (time.endsWith("H")) { - exp.add(Calendar.HOUR, Integer.valueOf(StringUtils - .remove(time, 'H'))); - } else if (time.endsWith("M")) { - exp.add(Calendar.MINUTE, Integer.valueOf(StringUtils.remove(time, - 'M'))); - } else if (time.endsWith("S")) { - exp.add(Calendar.MILLISECOND, Integer.valueOf(StringUtils.remove( - time, 'S')) * 1000); - } - return exp.getTimeInMillis(); - } - - /** - * Converts a string denoting an amount of time into seconds. Strings are - * expected to follow this form where # equals a digit: #M The following are - * permitted for denoting time: H = hours, M = minutes, S = seconds - * - * @param time time - * @return time in seconds - */ - public static int convertStringToTimeSeconds(String time) { - int result = 0; - if (time.endsWith("H")) { - int hoursToAdd = Integer.valueOf(StringUtils.remove(time, 'H')); - result = (60 * 60) * hoursToAdd; - } else if (time.endsWith("M")) { - int minsToAdd = Integer.valueOf(StringUtils.remove(time, 'M')); - result = 60 * minsToAdd; - } else if (time.endsWith("S")) { - int secsToAdd = Integer.valueOf(StringUtils.remove(time, 'S')); - result = secsToAdd; - } - return result; - } - - /** - * Converts a string denoting an amount of time into milliseconds. Strings - * are expected to follow this form where # equals a digit: #M The following - * are permitted for denoting time: H = hours, M = minutes, S = seconds - * - * @param time time - * @return time in milliseconds - */ - public static long convertStringToTimeMillis(String time) { - long result = 0; - if (time.endsWith("H")) { - long hoursToAdd = Integer.valueOf(StringUtils.remove(time, 'H')); - result = ((1000 * 60) * 60) * hoursToAdd; - } else if (time.endsWith("M")) { - long minsToAdd = Integer.valueOf(StringUtils.remove(time, 'M')); - result = (1000 * 60) * minsToAdd; - } else if (time.endsWith("S")) { - long secsToAdd = Integer.valueOf(StringUtils.remove(time, 'S')); - result = 1000 * secsToAdd; - } - return result; - } - - /** - * Converts a string denoting an amount of bytes into an integer value. - * Strings are expected to follow this form where # equals a digit: #M The - * following are permitted for denoting binary size: K = kilobytes, M = - * megabytes, G = gigabytes - * - * @param memSize memory - * @return size as an integer - */ - public static int convertStringToMemorySizeInt(String memSize) { - int result = 0; - if (memSize.endsWith("K")) { - result = Integer.valueOf(StringUtils.remove(memSize, 'K')) * 1000; - } else if (memSize.endsWith("M")) { - result = Integer.valueOf(StringUtils.remove(memSize, 'M')) * 1000 * 1000; - } else if (memSize.endsWith("G")) { - result = Integer.valueOf(StringUtils.remove(memSize, 'G')) * 1000 * 1000 * 1000; - } - return result; - } - - /** - * Converts a string denoting an amount of bytes into an long value. Strings - * are expected to follow this form where # equals a digit: #M The following - * are permitted for denoting binary size: K = kilobytes, M = megabytes, G = - * gigabytes - * - * @param memSize memory size - * @return size as an long - */ - public static long convertStringToMemorySizeLong(String memSize) { - long result = 0; - if (memSize.endsWith("K")) { - result = Long.valueOf(StringUtils.remove(memSize, 'K')) * 1000; - } else if (memSize.endsWith("M")) { - result = Long.valueOf(StringUtils.remove(memSize, 'M')) * 1000 * 1000; - } else if (memSize.endsWith("G")) { - result = Long.valueOf(StringUtils.remove(memSize, 'G')) * 1000 * 1000 * 1000; - } - return result; - } - - /** - * Quick time converter to keep our timestamps compatible with PHP's time() - * (seconds) - * @return time in seconds - */ - public static Integer getCurrentTimeSeconds() { - return convertMillisToSeconds(System.currentTimeMillis()); - } - - /** - * Quick time converter to keep our timestamps compatible with PHP's time() - * (seconds) - * @param millis milliseconds - * @return seconds - */ - public static Integer convertMillisToSeconds(Long millis) { - return Long.valueOf(millis / 1000).intValue(); - } - -} diff --git a/src/main/java/org/red5/server/util/ScopeUtils.java b/src/main/java/org/red5/server/util/ScopeUtils.java deleted file mode 100644 index 43212edfb..000000000 --- a/src/main/java/org/red5/server/util/ScopeUtils.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * RED5 Open Source Flash Server - http://code.google.com/p/red5/ - * - * Copyright 2006-2014 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.util; - -import java.lang.reflect.Field; -import java.util.Arrays; - -import org.red5.server.api.IContext; -import org.red5.server.api.persistence.IPersistable; -import org.red5.server.api.scope.IBasicScope; -import org.red5.server.api.scope.IScope; -import org.red5.server.api.scope.IScopeHandler; -import org.red5.server.api.scope.IScopeService; -import org.red5.server.api.scope.ScopeType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; - -/** - * Collection of utilities for working with scopes - */ -public class ScopeUtils { - - private static final Logger log = LoggerFactory.getLogger(ScopeUtils.class); - - private static final String SERVICE_CACHE_PREFIX = "__service_cache:"; - - /** - * Constant for slash symbol - */ - private static final String SLASH = "/"; - - /** - * Resolves scope for specified scope and path. - * - * @param from Scope to use as context (to start from) - * @param path Path to resolve - * @return Resolved scope - */ - public static IScope resolveScope(IScope from, String path) { - log.debug("resolveScope from: {} path: {}", from.getName(), path); - IScope current = from; - if (path.startsWith(SLASH)) { - current = ScopeUtils.findRoot(current); - path = path.substring(1, path.length()); - } - if (path.endsWith(SLASH)) { - path = path.substring(0, path.length() - 1); - } - log.trace("Current: {}", current); - String[] parts = path.split(SLASH); - if (log.isTraceEnabled()) { - log.trace("Parts: {}", Arrays.toString(parts)); - } - for (String part : parts) { - log.trace("Part: {}", part); - if (part.equals(".")) { - continue; - } - if (part.equals("..")) { - if (!current.hasParent()) { - return null; - } - current = current.getParent(); - continue; - } - if (!current.hasChildScope(part)) { - return null; - } - current = current.getScope(part); - log.trace("Current: {}", current); - } - return current; - } - - /** - * Finds root scope for specified scope object. Root scope is the top level - * scope among scope's parents. - * - * @param from Scope to find root for - * @return Root scope object - */ - public static IScope findRoot(IScope from) { - IScope current = from; - while (current.hasParent()) { - current = current.getParent(); - } - return current; - } - - /** - * Returns the application scope for specified scope. Application scope has - * depth of 1 and has no parent. - * - * See isApp method for details. - * - * @param from Scope to find application for - * @return Application scope. - */ - public static IScope findApplication(IScope from) { - IScope current = from; - while (current.hasParent() && !current.getType().equals(ScopeType.APPLICATION)) { - current = current.getParent(); - } - return current; - } - - /** - * Check whether one scope is an ancestor of another - * - * @param from Scope - * @param ancestor Scope to check - * @return true if ancestor scope is really an ancestor of - * scope passed as from parameter, false otherwise. - */ - public static boolean isAncestor(IBasicScope from, IBasicScope ancestor) { - IBasicScope current = from; - while (current.hasParent()) { - current = current.getParent(); - if (current.equals(ancestor)) { - return true; - } - } - return false; - } - - /** - * Checks whether scope is root or not - * - * @param scope Scope to check - * @return true if scope is root scope (top level scope), - * false otherwise. - */ - public static boolean isRoot(IBasicScope scope) { - return !scope.hasParent(); - } - - /** - * Check whether scope is the global scope (level 0 leaf in scope tree) or - * not - * - * When user connects the following URL: rtmp://localhost/myapp/foo/bar then / - * is the global level scope, myapp is app level, foo is room level and bar - * is room level as well (but with higher depth level) - * - * @param scope Scope to check - * @return true if scope is the global scope, - * false otherwise. - */ - public static boolean isGlobal(IBasicScope scope) { - return scope.getType().equals(ScopeType.GLOBAL); - } - - /** - * Check whether scope is an application scope (level 1 leaf in scope tree) - * or not - * - * @param scope Scope to check - * @return true if scope is an application scope, - * false otherwise. - */ - public static boolean isApp(IBasicScope scope) { - return scope.getType().equals(ScopeType.APPLICATION); - } - - /** - * Check whether scope is a room scope (level 2 leaf in scope tree or lower, - * e.g. 3, 4, ...) or not - * - * @param scope Scope to check - * @return true if scope is a room scope, false - * otherwise. - */ - public static boolean isRoom(IBasicScope scope) { - return scope.getType().equals(ScopeType.ROOM); - } - - /** - * Returns scope service by bean name. See overloaded method for details. - * - * @param scope scope - * @param name name - * @return object - */ - protected static Object getScopeService(IScope scope, String name) { - return getScopeService(scope, name, null); - } - - /** - * Returns scope services (e.g. SharedObject, etc) for the scope. Method - * uses either bean name passes as a string or class object. - * - * @param scope - * The scope service belongs to - * @param name - * Bean name - * @param defaultClass - * Class of service - * @return Service object - */ - protected static Object getScopeService(IScope scope, String name, Class defaultClass) { - if (scope != null) { - final IContext context = scope.getContext(); - ApplicationContext appCtx = context.getApplicationContext(); - Object result; - if (!appCtx.containsBean(name)) { - if (defaultClass == null) { - return null; - } - try { - result = defaultClass.newInstance(); - } catch (Exception e) { - log.error("{}", e); - return null; - } - } else { - result = appCtx.getBean(name); - } - return result; - } - return null; - } - - /** - * Returns scope service that implements a given interface. - * - * @param scope The scope service belongs to - * @param intf The interface the service must implement - * @return Service object - */ - public static Object getScopeService(IScope scope, Class intf) { - return getScopeService(scope, intf, null); - } - - public static Object getScopeService(IScope scope, Class intf, boolean checkHandler) { - return getScopeService(scope, intf, null, checkHandler); - } - - /** - * Returns scope service that implements a given interface. - * - * @param scope The scope service belongs to - * @param intf The interface the service must implement - * @param defaultClass Class that should be used to create a new service if no service was found. - * @return Service object - */ - public static Object getScopeService(IScope scope, Class intf, Class defaultClass) { - return getScopeService(scope, intf, defaultClass, true); - } - - public static Object getScopeService(IScope scope, Class intf, Class defaultClass, boolean checkHandler) { - if (scope == null || intf == null) { - return null; - } - // We expect an interface - assert intf.isInterface(); - - String attr = IPersistable.TRANSIENT_PREFIX + SERVICE_CACHE_PREFIX + intf.getCanonicalName(); - if (scope.hasAttribute(attr)) { - // return cached service - return scope.getAttribute(attr); - } - - Object handler = null; - if (checkHandler) { - IScope current = scope; - while (current != null) { - IScopeHandler scopeHandler = current.getHandler(); - if (intf.isInstance(scopeHandler)) { - handler = scopeHandler; - break; - } - if (!current.hasParent()) { - break; - } - current = current.getParent(); - } - } - - if (handler == null && IScopeService.class.isAssignableFrom(intf)) { - // we've got an IScopeService, try to lookup bean - Field key = null; - Object serviceName = null; - try { - key = intf.getField("BEAN_NAME"); - serviceName = key.get(null); - if (serviceName instanceof String) { - handler = getScopeService(scope, (String) serviceName, defaultClass); - } - } catch (Exception e) { - log.debug("No string field 'BEAN_NAME' in that interface"); - } - } - if (handler == null && defaultClass != null) { - try { - handler = defaultClass.newInstance(); - } catch (Exception e) { - log.error("", e); - } - } - // cache service - scope.setAttribute(attr, handler); - return handler; - } - -}