JUV RTMP Client Developer Guide

Version: 1.6

Copyright © 2012 Smaxe Ltd


Table of Contents

1. Introduction
2. Getting Started
3. Architecture
4. Usage examples
5. FAQ

List of Examples

4.1. Set license key
4.2. Create and configure NetConnection
4.3. Connect to the server and invoke server method
4.4. Connect to the remote shared object
4.5. Publish local FLV file as live stream
4.6. Publish audio source as live stream

Chapter 1. Introduction

JUV RTMP Client is a lightweight library that provides RTMP/RTMPT-enabled server access API for Java applications.

Features:

Related:
JUV RTMP Client (J2ME edition) - J2ME RTMP client
JUV RTMP Researcher - RTMP protocol debugger application
JUV RTMP Tester - RTMP functional/regression/load testing library


Chapter 2. Getting Started

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

It requires the license key: either Evaluation Key (get it) or Developer Key (buy it).


Chapter 3.  Architecture

Java RTMP Client API is very similar to the API provided by Adobe® in the ActionScript's flash.net.* package.
There are 3 core classes defined in com.smaxe.uv.client.rtmp package:


Connection configuration parameters are defined in INetConnection.Configuration class.
These are:
Library threading model is defined by 2 thread-specific parameters passed to the NetConnection constructor (public NetConnection(final Map configuration, final ExecutorService dispatcher, final ScheduledExecutorService scheduler)): Please make sure that you process shared object changes (ISharedObject.IListener methods) and video notification (IVideo methods) very fast or in a separate thread to prevent scheduler thread blocking.

com.smaxe.uv.invoker.IMethodInvoker

Every method invoked by the server is processed by the IMethodInvoker instance.


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);
}

The default implementation is based on reflection (com.smaxe.uv.invoker.support.MethodInvoker).

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);
}

A simple implementation of IObjectCreator may look like:

/**
 * Simple class we want to send to the RTMP server.
 */
public class CustomClass extends Object
{
    public int field1;
    public String field2;
}

/**
 * IObjectCreator implementation.
 */
public class ObjectCreator implements IObjectCreator
{
    public ClassObject toClassObject(final Object o)
    {
        if (o instanceof CustomClass)
        {
            final CustomClass cc = (CustomClass) o;

            Map propertyValues = new HashMap();
            
            propertyValues.put("field1", cc.field1);
            propertyValues.put("field2", cc.field2);
            
            return new ClassObject("CustomClass", propertyValues);
        }
        else
        {
            // process other classes
        }
    }

    public Object toObject(final ClassObject co)
    {
        if ("CustomClass".equals(co.className))
        {
            CustomClass cc = new CustomClass();
            
            cc.field1 = (Integer) co.propertyValues.get("field1");
            cc.field2 = (String) co.propertyValues.get("field2");

            return cc;
        }
        else
        {
            // process other classes
        }
    }
    
    // other methods necessary for amf3 serializing/deserializing
}

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);
}

java.net.Proxy

You can define proxy server to use. Proxy instance is used as Socket constructor parameter. The default value is Proxy.NO_PROXY.

Chapter 4. Usage examples

Example 4.1. Set license key

 
 com.smaxe.uv.client.rtmp.License.setKey("00000-00000-00000-00000-00000");
 

Example 4.2. Create and configure NetConnection

 
 NetConnection connection = new NetConnection();
 // configure 
 connection.configuration().put(NetConnection.Configuration.INACTIVITY_TIMEOUT, -1 /*seconds, -1 means indefinite timeout*/);
 connection.configuration().put(NetConnection.Configuration.PAGE_URL, "www.myhost.com/app");
 // etc...

Example 4.3. Connect to the server and invoke server method


 NetConnection connection = new NetConnection();

 // configure connection (if necessary)

 // set callback object
 connection.client(new Object()
 {
     // note: this method is invoked in the Dispatch-Thread
     public boolean test()
     {
         System.out.println("NetConnectionCallback#test");
         
         return true;
     }
 });
 
 // add event listener to get notified about connection status change
 connection.addEventListener(new NetConnection.ListenerAdapter()
 {
     // note: this method is invoked in the Dispatch-Thread
     public void onNetStatus(final INetConnection source, final Map info)
     {
         final Object code = info.get("code");
         
         if (NetConnection.CONNECT_SUCCESS.equals(code))
         {
             System.out.println("Connected to the server!");
         }
     }
 });
 
 connection.connect("rtmp://localhost:1935/live", "arg1", "arg2" /*arguments*/);
 
 // wait till NetConnection.CONNECT_SUCCESS message is received
 // ... waiting ...
 
 // invoke server method
 connection.call("testConnection", new com.smaxe.uv.Responder()
 {
     // note: this method is invoked in the Dispatch-Thread
     public void onResult(final Object result)
     {
         System.out.println("Method testConnection result: " + result);
     }
     
     // note: this method is invoked in the Dispatch-Thread
     public void onStatus(final Map status)
     {
         System.out.println("Method testConnection status: " + status);
     }
 }, "arg3", "arg4" /*arguments*/);
 

Example 4.4. Connect to the remote shared object

 
 NetConnection connection = new NetConnection();
 
 // ... configure and connect ... 
 
 SharedObject so = new SharedObject(name);
 
 so.addEventListener(new ISharedObject.ListenerAdapter()
 {
     // note: this method is invoked in the Scheduler-Thread
     public void onSync(ISharedObject source, final List changeList)
     {
         for (ISharedObject.Change change : changeList)
         {
             switch (change.code)
             {
                 case ISharedObject.Change.CONNECT:
                 {
                     // connected to the remote shared object
                 } break;
                 case ISharedObject.Change.CHANGE:
                 {
                     // change.attribute data was changed from the change.oldValue
                     // to the change.newValue
                 } break;
                 case ISharedObject.Change.STATUS:
                 {
                     // shared object status notification
                 } break;
             }
         }
     }
 });
 
 so.connect(connection, name);
 

Example 4.5. Publish local FLV file as live stream

 
 NetConnection connection = new NetConnection();
 
 // ... configure and connect ... 
 
 NetStream stream = new NetStream(connection);
 
 stream.addEventListener(new NetStream.ListenerAdapter()
 {
     @Override
     public void onNetStatus(final INetStream source, final Map info)
     {
         final String code = (String) info.get("code");
         
         if (NetStream.PUBLISH_START.equals(code))
         {
         }
         else
         if (NetStream.UNPUBLISHED_SUCCESS.equals(code))
         {
         }
     }
 });
 
 final MediaStreamController controller = new MediaStreamController();
 
 stream.attachAudio(new MediaStreamMicrophone(controller));
 stream.attachCamera(new MediaStreamCamera(controller), -1);
 
 stream.publish("streamName", NetStream.LIVE);
 
 controller.play(new FlvFileMediaStream("stream.flv"), -1 /*play the whole file*/);
 

Example 4.6. Publish audio source as live stream (ExRtmpVoicePublisher.java)

 
 public final class MyMicrophone extends AbstractMicrophone
 {
     // starts microphone capture
     public void start()
     {
         new Thread(new Runnable()
         {
             while (true)
             {
                 byte[] data = getAudioData(); // returns audio data captured from your source
                 
                 fireOnAudioData(new MediaDataByteArray(33 /*relative time*/, new ByteArray(data)));
                 
                 try
                 {
                     Thread.sleep(33);
                 {
                 catch (Exception e) {}
             }
         }).start();
     }
 }
 
 NetConnection connection = new NetConnection();
 
 // ... configure and connect ... 
 
 NetStream stream = new NetStream(connection);
 
 stream.addEventListener(new NetStream.ListenerAdapter()
 {
     @Override
     public void onNetStatus(final INetStream source, final Map info)
     {
         final String code = (String) info.get("code");
         
         if (NetStream.PUBLISH_START.equals(code))
         {
         }
         else
         if (NetStream.UNPUBLISHED_SUCCESS.equals(code))
         {
         }
     }
 });
 
 MyMicrophone mic = new MyMicrophone();
 
 stream.attachAudio(mic);
 
 stream.publish("streamName", NetStream.LIVE);
 
 mic.start();
 

Chapter 5. FAQ

What does "play and publish audio/video streams" mean?

The library provides protocol layer implementation only and doesn't contain any codec implementation. The phrase means that you'll get encoded audio/video data for playing it, i.e. you need codecs to decode the audio/video data stream, and you need to encode your data to publish it to the server.

Ask your question