JUV RTMP Tester Developer Guide

Version: 1.1


Table of Contents

1. Introduction
2. Getting Started
3. Architecture
4. Using JUV RTMP Tester for testing server-side logic/performance
5. FAQ
6. Support

List of Examples

4.1. Set license key
4.2. Simplest script (just connects to the server)
4.3. Script that connects to the server using connection parameters
4.4. Script that plays stream and disconnects on playback stop
4.5. Script that publishes stream and disconnects on publish stop
4.6. Script that connects to the remote shared object
4.7. RtmpTester executes ConnectSharedObjectScript script
4.8. Get bandwidth used by script as a function of time


Chapter 1. Introduction

JUV RTMP Tester is a lightweight library/tool that lets you execute functional/regression/load testing of the RTMP server side functionality.

Related:
JUV RTMP Client - J2SE RTMP client
JUV RTMP Client (J2ME edition) - J2ME RTMP client
JUV RTMP Researcher - RTMP debug tool


Chapter 2. Getting Started

JUV RTMP Tester library consists of the only JAR file (no dependencies) that should be placed in the class path.

Library requires a license key. If it is not set or invalid then it operates in evaluation (lite) mode.
Evaluation version has next limitations:

  • only "localhost" connection is allowed
  • only one client script executed at a time
  • client session duration is limited to 1 minute
You need to buy license key to remove evaluation mode limitations.


Chapter 3. Architecture

There are 2 core entities (classes) provided by the library.

  • com.smaxe.app.uv.agent.RtmpClientScript - implements client side logic.
  • com.smaxe.app.uv.loadtester.RtmpTester - responsible for executing client scripts (RtmpClientScript subclasses instances).
There are also classes inherited from the JUV RTMP Client library:

com.smaxe.uv.invoker.IMethodInvoker

Every method invoked by the server is processed by the IMethodInvoker instance. The default implementation (com.smaxe.uv.invoker.support.MethodInvoker) is based on reflection.


public interface IMethodInvoker
{
    /**
     * Invokes method with the args of the object o.
     * The result is returned through the {@link ICallback callback}.
     * 
     * @param o object which method is invoked
     * @param method method to invoke
     * @param callback callback to receive invocation result
     * @param args method arguments
     */
    public void invoke(final Object o, final String method, final ICallback callback, final Object... args);
}

com.smaxe.uv.amf.IObjectCreator

Every custom Java object that is sent/received to/from the server must be presented as ClassObject, i.e. class name + {property : property value} map. The IObjectCreator implementation is responsible for the such transformation.


public interface IObjectCreator
{
    /**
     * Converts {@link ClassObject co} to the custom type object.
     * 
     * @param co ClassObject instance
     * @return custom object
     */
    public Object toObject(final ClassObject co);
    
    /**
     * Converts {@link Object o} to the {@link ClassObject} instance.
     * 
     * @param o object to represent as ClassObject
     * @return ClassObject instance
     */
    public ClassObject toClassObject(final Object o);
}

com.smaxe.logger.ILogger

The library doesn't use any specific logging system. It provides a simple interface that lets you either integrate the library with any logging library or use ILogger implementations provided in the com.smaxe.logger.support package.


public interface ILogger
{
    /**
     * Logs the message.
     * 
     * @param level message level
     * @param message message to log
     * @param t thrown exception, null if the exception is not thrown
     * @param args optional arguments
     */
    public void log(final int level, final String message, final Throwable t, final Object... args);
} 
 


Chapter 4. Using JUV RTMP Tester for testing server-side logic/performance

Example 4.1. Set license key

Tool requires a license key. If it is not set or invalid then it operates in evaluation (lite) mode.
Evaluation version has next limitations:
  • only "localhost" connection is allowed
  • only one client script executed at a time
  • client session duration is limited to 1 minute
 
 com.smaxe.app.uv.loadtester.RtmpTester.setKey("00000-00000-00000-00000-00000");
 


Lets start with script samples.
The simplest script that just connects to the server looks like:

Example 4.2. Simplest script (just connects to the server)


/**
 * SimpleScript
 * Behaviour:
 *  - Client just connects to the server.
 */
public final class SimpleScript extends RtmpClientScript
{
    /**
     * Constructor.
     */
    public SimpleScript()
    {
    }
}


Example 4.3. Script that connects to the server using connection parameters


/**
 * SimpleScript2
 * Behaviour:
 *  - Client connects to the server using connection parameters.
 */
public final class SimpleScript2 extends RtmpClientScript
{
    /**
     * Constructor.
     */
    public SimpleScript2()
    {
    }
    
    @Override
    public Object[] onStart()
    {
        return new Object[] {"username", "password", "age"}
    }
}


Example 4.4. Script that plays stream and disconnects on playback stop

 
/**
 * PlayStreamScript
 * Behaviour:
 *  - Client connects to the server and starts stream playback on successfull connection
 * (stream name is defined in the "streamToPlay" property)
 *  - Client disconnects from the server after stream playback is stopped/completed.
 */
public final class PlayStreamScript extends RtmpClientScript
{
    /**
     * Constructor.
     */
    public PlayStreamScript()
    {
    }
    
    @Override
    public void onConnect(final String code, final Map info)
    {
        super.onConnect(code, info);
        
        play((String) properties().get("streamToPlay"));
    }
    
    @Override
    public void onPlayStop(final String stream, final MediaStreamInfo info)
    {
        super.onPlayStop(stream, info);
        
        disconnect();
    }
}

Example 4.5. Script that publishes stream and disconnects on publish stop


/**
 * PublishStreamScript
 * Behaviour:
 *  - Client connects to the server and publishes stream on successfull connection
 * (stream name is defined in the "streamToPublish" property, stream source is a local flv file).
 *  - Client disconnects from the server after stream publish is stopped/completed.
 */
public final class PublishStreamScript extends RtmpClientScript
{
    /**
     * Constructor.
     */
    public PublishStreamScript()
    {
    }
    
    @Override
    public void onConnect(final String code, final Map info)
    {
        super.onConnect(code, info);
        
        publish((String) properties().get("streamToPublish") + "_" + getClientId(),
                -1, PUBLISH_MODE_LIVE);
    }
    
    @Override
    public void onPublishStop(final String stream)
    {
        super.onPublishStop(stream);
        
        disconnect();
    }
} 

Example 4.6. Script that connects to the remote shared object

 
/**
 * ConnectSharedObjectScript
 * Behaviour:
 *  - Client connects to the server and connects to shared object "test"
 *  - Client sets "attribute" value to 1 on shared object connection.
 *  - Client increments "attribute" value by 1 on every "attribute" value update and
 * disconnects if value exceeds 15.
 */
public final class ConnectSharedObjectScript extends RtmpClientScript
{
    /**
     * Constructor.
     */
    public ConnectSharedObjectScript()
    {
    }
    
    @Override
    public void onConnect(final String code, final Map info)
    {
        super.onConnect(code, info);
        
        connectSharedObject("test");
    }
    
    @Override
    public void onSharedObjectConnect(final String name)
    {
        super.onSharedObjectConnect(name);
        
        getSharedObjectData(name).put("attribute", 1 /*value*/);
    }
    
    @Override
    public void onSharedObjectChange(final String name,
            final String attribute, final Object oldValue, final Object newValue)
    {
        super.onSharedObjectChange(name, attribute, oldValue, newValue);
        
        if ("test".equals(name))
        {
            if ("attribute".equals(attribute))
            {
                getSharedObjectData(name).put(attribute, ((Integer) newValue).intValue() + 1);
                
                if ((Integer) getSharedObjectData(name).get(attribute) > 15)
                {
                    closeSharedObject(name);
                    disconnect();
                }
                else
                if ((Integer) getSharedObjectData(name).get(attribute) > 10)
                {
                    sendSharedObjectAction(name, "notifyEveryone", new Object[] {"Attribute value exceeded 10!"});
                }
            }
        }
    }
    
    @Override
    public void onSharedObjectDisconnect(final String name)
    {
        super.onSharedObjectDisconnect(name);
    }
} 

com.smaxe.app.uv.loadtester.RtmpTester class is responsible for client script execution.

Example 4.7. RtmpTester executes ConnectSharedObjectScript script


RtmpTester tester = new RtmpTester();

// note: set license key before tester.emulateClient(*) invocation

tester.emulateClient("rtmp://localhost/live", new RtmpClientInfo(ConnectSharedObjectScript.class.getCanonicalName), null /*callback*/);


RtmpTester.emulateClient() parameters are:
  • url - url to connect
  • info - RtmpClientInfo instance that provides tester with client info: script to use, encoding, session time, properties, etc
  • callback - RtmpTester.ICallback instance that notifies about client connection/disconnection

Example 4.8. Get bandwidth used by script as a function of time


public final class RtmpTesterCallback extends Object implements RtmpTester.ICallback
{
    /**
     * Constructor.
     */
    public RtmpTesterCallback()
    {
    }
	
    /**
     * Notifies about disconnection from the server.
     * You can use {@link RtmpTester.IClientSessionInfoProvider provider}
     * to get session details.
     */
    public void onDisconnect(final RtmpTester.IClientSessionInfoProvider provider)
    {
        System.out.println("RtmpTesterCallback#onDisconnect: " + provider.id());
        
        // NOTE:
        // RtmpTester can provide you with Bandwidth as a function of time
        System.out.println("Bandwidth(t):");
        for (Map entry : provider.getEntities(RtmpTester.IClientSessionInfoProvider.BANDWIDTH, null /*selector*/))
        {
            // bandwidth entry represents a map (field name -> value)
            // for example,
            // {stimestamp=1297, readBytes=87237, writtenBytes=3420, readPackets=221, writtenPackets=5}
            // where stimestamp is session timestamp
            
            System.out.println(entry);
        }
    }
}


Chapter 5. FAQ

Ask your question