Red5 Pro Scopes


1. What are scopes

A scope represents a virtual separation of resources on your server. Red5 Pro resources such as streams, connections, SharedObject’s are all managed within the context of scopes.

If you know about the golden days of the Adobe Flash Platform, you would remember the popularity of socket servers for games. Socket servers were designed mainly for games and chat applications. A chat application with multiple rooms is hence a very good example of scopes. For example, there could be main chat room and then there can be other private/public rooms. These rooms actually provide a virtual separation of resources to avoid collision and at help in better management of resources.

Another close analogy would be a hard drive and its partitions. The purpose of creating partitions is nothing more than better management of files and folders for both user and the operating system itself. The fact however remains that partitions are logical and not physical in nature. All the partitions share same physical drive space but virtually they are separate spaces. So you can have a file called "red5server.txt" on each partition.

2. Types of scopes

Red5 Pro has a few different types of scopes. Based on the purpose for which the scope is created, following valid scope types are defined:

  • GLOBAL => The global scope is the root scope of the entire server. All applications are created and managed inside the global scope.
  • APPLICATION => The top level scope which represents the Red5 Pro application.
  • ROOM => A sublevel child scope within the application. All child scopes exist within the application scope. A ROOM scope can also be nested within another ROOM scope.
  • BROADCAST => The broadcast scope is created for a broadcast stream. Similar to the ROOM scope the BROADCAST scope lives within an APPLICATION scope and may be nested within a ROOM scope.
  • SHARED_OBJECT => A SHARED_OBJECT is created for a Red5 Pro SharedObject.

2.1 The global scope

The global scope is the core scope of the Red5 Pro server. It defaults to the name default. This is the first scope that gets created as the server starts up. The global scope is declared in the Red5 Pro configuration red5-default.xml located at RED5_HOME/webapps/red5-default.xml as a javabean.

This is just for information. The bean should not be edited unless you know what you are doing.

2.2 The web scope

The web.scope represents the APPLICATION scope. It is declared in every Red5 Pro application context file - red5-web.xml located at RED5_HOME/webapps/{appname}/WEB-INF/red5-web.xml. As the application scope is created, it gets injected with references to the server object, the global scope etc. The contextPath is the absolute path of the the scope which includes the global scope path.

Sample red5-web.xml of the live application

<bean id="web.scope" class="org.red5.server.scope.WebScope" init-method="register">
    <property name="server" ref="red5.server" />
    <property name="parent" ref="global.scope" />
    <property name="context" ref="web.context" />
    <property name="handler" ref="web.handler" />
    <property name="contextPath" value="${webapp.contextPath}" />
    <property name="virtualHosts" value="${webapp.virtualHosts}" / : PATH>
bean>

3. Scope Paths

Each scope is identified by a unique path where it is created within the context of the server. Given below are some examples to illustrate how scopes are laid out within the server.

GLOBAL SCOPE ::

/default

APPLICATION SCOPE :: live

/default/live

A ROOM INSIDE THE APPLICATION SCOPE :: myroom

/default/live/myroom

4. Monitoring scope creation: The IScopeListener

Sometimes you may want to listen for application creation or removal. A good use case would be a Plugin which needs to be attached to one or more applications on the server. To do this, we need to listen for application creation as the server starts up and runs your plugin. The code snippet below shows how we can use a IScopeListener to listen for application startup and then do something with the scope object.

private void monitorApplicationStartup(){

    IScopeListener scopeListener = new IScopeListener() {

        @Override
        public void notifyScopeCreated(IScope scope) {

            if (scope.getType() == ScopeType.APPLICATION) {
               log.info("application started " + scope.getName());
            }

        }

        @Override
        public void notifyScopeRemoved(IScope scope) {

            if (scope.getType() == ScopeType.APPLICATION) {
                log.info("application stopped " + scope.getName());
            }
        }

    };

    server.addListener(scopeListener);
}

A server side application showcasing the usage of an IScopeListener object is available on github as an example.

5. Fetching a list of scope names from a Red5 Pro application

if you wish to fetch the names of all child Scopes created on your application, you can use the MultiThreadedApplicationAdapter api. The MultiThreadedApplicationAdapter extends the StatefulScopeWrappingAdapter. you can use the getChildScopeNames http://red5.org/javadoc/red5-server-common/org/red5/server/adapter/StatefulScopeWrappingAdapter.html#getChildScopeNames--

appAdapter.getChildScopeNames()

6. The IApplication interface: Intercepting connections to scopes

The IApplication interface in Red5 Pro is used to tap into the Red5 Pro application adapter's connection & scope events. The IApplication interface can be immensely useful in tracking/validating connections on scopes.

You can create a IApplication implementation by creating a java class which implements the IApplication interface. To register the implementation to an application use the addListener) method of the MultiThreadedApplicationAdapter class.

A sample implementation of the IApplication interface is given below:

public class ApplicationMonitor implements IApplication {

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

    @Override
    public boolean appStart(IScope app) {
        log.info("appStart "  +app.toString());
        return true;
    }

    @Override
    public boolean appConnect(IConnection conn, Object[] params) {
        log.info("appConnect : Scope "  +conn.getScope().toString());
        return true;
    }

    @Override
    public boolean appJoin(IClient client, IScope app) {
        log.info("appJoin : Scope "  +app.toString());
        return true;
    }

    @Override
    public void appDisconnect(IConnection conn) {
        log.info("appDisconnect : Scope "  +conn.getScope().toString());
    }

    @Override
    public void appLeave(IClient client, IScope app) {
        log.info("appDisconnect : Scope "  + app.toString());
    }

    @Override
    public void appStop(IScope app) {
        log.info("appStop : Scope "  + app.toString());
    }

    @Override
    public boolean roomStart(IScope room) {
        log.info("roomStart : Scope "  + room.toString());
        return true;
    }

    @Override
    public boolean roomConnect(IConnection conn, Object[] params) {
        log.info("roomConnect : Scope "  + conn.getScope().toString());
        return true;
    }

    @Override
    public boolean roomJoin(IClient client, IScope room) {
        log.info("roomJoin : Scope "  + room.toString());
        return true;
    }

    @Override
    public void roomDisconnect(IConnection conn) {
        log.info("roomDisconnect : Scope "  + conn.getScope().toString());
    }

    @Override
    public void roomLeave(IClient client, IScope room) {
        log.info("roomLeave : Scope "  + room.toString());
    }

    @Override
    public void roomStop(IScope room) {
        log.info("roomLeave : Scope "  + room.toString());
    }

}

The AppMonitor class can then be registered with your Application Adapter in your Application class like this:

@Override
public boolean appStart(IScope arg0) {
        this.addListener(new ApplicationMonitor());
        return super.appStart(arg0);
}

When attempting to register from a plugin, you need to first access the MultiThreadedApplicationAdapter class from the application scope using the getHandler method of the IScope interface. A very important thing to notice is that some of the method here return a Boolean value. If you return a false or throw a ClientRejectedException then the client is unable to proceed through the connection process.

A detailed example application is available on github for reference.You can run the example on a live server instance to observe the sequence of method calls. The sample AppMonitor class shows how get information about a scope whenever a client attempts to connect to the server application.

7. Finding out where the client is connected

At anytime if you wish to find out which scope the current client is connected to, you can use the getScope() method of IConnection interface.

Example:

IConnection connection = Red.getConnectionLocal();
IScope scope = connection.getScope();

Every connection is a IConnection in Red5 Pro. Red.getConnectionLocal() is a static method which returns reference to the current client connection with respect to the executing thread.

8. Bonus: The ScopeUtils class

The ScopeUtils is a static class provided in red5 that can be extremely useful when dealing with scopes.

Some of the common utilities include :

  1. Resolving the parent scope of a sub-scope.
  2. Tracing back to the application scope from any IScope in an application.
  3. Locating the global scope in an application
  4. Checking what type a given scope is of.
  5. Finding a scope within a scope

8.1 Finding a sub-scope

[INFO] [NioProcessor-3] org.red5.scopes.examples.appmonitor.ApplicationMonitor - roomStart : Scope Scope [name=lastroom, path=/default/iapplication-demo/room, type=ROOM, autoStart=true, creationTime=1497875754784, depth=3, enabled=true, running=false]

The sample log statement above is the value of the scope when we attempt inside the roomStart method when we attempt to connect to a Red5 Pro application iapplication-demo when connecting using the url - rtmp://localhost/iapplication-demo/room/lastroom.

To be able to find the scope named lastroom from anywhere in your application at any time, you can use the ScopeUtils method - resolveScope.

The method requires two parameters. A parent scope and the relative path to the subscope to be resolved. For exampel to resolve the IScope object for lastroom you do the following.

IScope scope = ScopeUtils.resolveScope(appScope, "room/lastroom");

Where appScope is the application scope.

9. Conclusion

Scopes are the core object of the Red5 Pro architecture. Scopes are logical boundaries within which we define resources. If you are dealing with a simple streaming concept, you may not need to fiddle with scopes at all. However, as you dive deeper and requirements get more complex, you will see how having scopes simplify things a lot.