• How to extend Wowza Streaming Engine using Java

    Wowza Streaming Engine™ media server software is built using Java technology and can be extended by writing custom Java classes that are loaded dynamically by the server at runtime. Several integration points can be used to extend the server: custom server-side extensions (also referred to as "modules"), HTTP Providers, and listeners. This article explores each of these integration points and provides quick examples.

    Contents


    Custom module classes
    HTTP provider classes
    Event listeners

    Custom module classes


    Server-side modules are Java classes that are configured on a per-application basis and are dynamically loaded at when an application instance starts. Typically, module classes are bound to .jar files that are located in the Wowza Streaming Engine software installation. Modules can leverage third-party libraries or built-in Java functionality if the dependent .jar files are copied to the [install-dir]/lib folder in the Wowza Streaming Engine installation. Modules are added to an application configuration by adding an entry to the Modules list for an application in Wowza Streaming Engine Manager.

    Notes:
    • If you want to add a module to an application configuration, and the application is running, you must restart the application for the changes to take effect.

    • If you modify an installed module, you must restart Wowza Streaming Engine for the changes to take effect.
    Let's start by creating our first module. It'll have two methods: the event method onAppStart and the custom method doSomething (Event methods and Custom methods are discussed later):
    package com.mycompany.module;
    
    import com.wowza.wms.module.*;
    import com.wowza.wms.client.*;
    import com.wowza.wms.amf.*;
    import com.wowza.wms.request.*;
    
    public class MyModule extends ModuleBase
    {
    	public void onAppStart(IApplicationInstance appInstance)
    	{
    		getLogger().info("onAppStart");
    	}
    
    	public void doSomething(IClient client, RequestFunction function, 
    					AMFDataList params)
    	{
    		getLogger().info("doSomething");
    	} 
    }
    To add this module to an application configuration, go to the application in Wowza Streaming Engine Manager and use the Add New Module dialog box to add the following entry to the end of the Modules list for the application:



    Each module must have a unique Name in the Modules list. The Description information provides a detailed description of the module and isn't used in any operations. The Class item is the fully qualified path to the Java class that provides the module's functionality. Java combines the package path in the first line of the module to the class name to form the class path.

    Event methods

    Event methods are invoked by the server based on events that occur during server processing. Event methods apply to all types of streaming: DASH Streaming, Apple HLS (Cupertino), Adobe HDS (San Jose), Microsoft Smooth Streaming, and RTSP/RTP streaming. Event methods are defined by the following interfaces:
    IModuleOnApp
    IModuleOnApp2
    IModuleOnConnect
    IModuleOnStream
    IModuleOnHTTPSession
    IModuleOnRTPSession
    IModuleOnHTTPCupertinoStreamingSession
    IModuleOnHTTPSmoothStreamingSession
    IModuleOnHTTPSanJoseStreamingSession
    IModuleOnHTTPMPEGDashStreamingSession
    IModuleOnHTTPCupertinoEncryption
    IModuleOnHTTPSmoothStreamingPlayReady
    IModuleOnHTTPMPEGDashEncryption
    Event methods that are defined in a module are invoked when an event occurs. If two modules implement on the onAppStart event method, the onAppStart method is invoked for both modules when a new application instance is created. Module methods are invoked in the order in which entries are listed in the Modules list for an application in Wowza Streaming Engine Manager. so the first entry in the Modules list is called first, the second entry is called next, and so on, down to the last item in the list. The rest of this section describes the event method interfaces and their corresponding methods.

    IModuleOnApp


    public void onAppStart(IApplicationInstance appInstance);
    public void onAppStop(IApplicationInstance appInstance);
    • onAppStart: Invoked when an application instance started.

    • onAppStop: Invoked when an application instance is stopped.

    IModuleOnApp2


    This interface extends IModuleOnApp, providing two additional methods:
    public void onAppCreate(IApplicationInstance appInstance);
    public void onAppDestroy(IApplicationInstance appInstance);
    • onAppCreate: Invoked when an application instance is created.

    • onappDestroy: Invoked when an application instance is destroyed.

    IModuleOnConnect


    public void onConnect(IClient client, 
    	RequestFunction function, AMFDataList params);
    public void onDisconnect(IClient client);
    public void onConnectAccept(IClient client);
    public void onConnectReject(IClient client);
    • onConnect: Invoked when Flash Player connects to an application instance.

    • onDisconnect: Invoked when Flash Player disconnects from an application instance.

    • onConnectAccept: Invoked when a Flash Player connection is accepted.

    • onConnectReject: Invoked when a Flash Player connection is refused.

    IModuleOnStream


    Note that the onStreamCreate event method is invoked before play or publish is called for this IMediaStream object so the IMediaStream object doesn't have a name. To implement a server listener that's invoked when actions occur on this IMediaStream object, see How to use event listeners.
    public void onStreamCreate(IMediaStream stream);
    public void onStreamDestroy(IMediaStream stream);
    • onStreamCreate: Invoked when a new IMediaStream object is created.

    • onStreamDestroy: Invoked when an IMediaStream object is closed.

    IModuleOnHTTPSession


    public void onHTTPSessionCreate(IHTTPStreamerSession httpSession);
    public void onHTTPSessionDestroy(IHTTPStreamerSession httpSession);
    • onHTTPSessionCreate: Invoked when an HTTP-based streaming session is created.

    • onHTTPSessionDestroy: Invoked when an HTTP-based streaming session is closed.

    IModuleOnRTPSession


    public void onRTPSessionCreate(RTPSession rtpSession);
    public void onRTPSessionDestroy(RTPSession rtpSession);
    • onRTPSessionCreate: Invoked when an RTP session is created.

    • onRTPSessionDestroy: Invoked when an RTP session is closed.

    IModuleOnHTTPCupertinoStreamingSession


    public void onHTTPCupertinoStreamingSessionCreate(
    	HTTPStreamerSessionCupertino httpCupertinoStreamingSession);
    public void onHTTPCupertinoStreamingSessionDestroy(
    	HTTPStreamerSessionCupertino httpCupertinoStreamingSession);
    • onHTTPCupertinoStreamingSessionCreate: Invoked when an Apple HLS (Cupertino) session is created.

    • onHTTPCupertinoStreamingSessionDestroy: Invoked when a Cupertino session is closed.

    IModuleOnHTTPSmoothStreamingSession


    public void onHTTPSmoothStreamingSessionCreate(
    	HTTPStreamerSessionSmoothStreamer httpSmoothStreamingSession);
    public void onHTTPSmoothStreamingSessionDestroy(
    	HTTPStreamerSessionSmoothStreamer httpSmoothStreamingSession);
    • onHTTPSmoothStreamingSessionCreate: Invoked when a Smooth Streaming session is created.

    • onHTTPSmoothStreamingSessionDestroy: Invoked when a Smooth Streaming session is closed.

    IModuleOnHTTPSanJoseStreamingSession


    public void onHTTPSanJoseStreamingSessionCreate(
    	HTTPStreamerSessionSanJoseStreamer httpSanJoseStreamingSession);
    public void onHTTPSanJoseStreamingSessionDestroy(
    	HTTPStreamerSessionSanJoseStreamer httpSanJoseStreamingSession);
    • onHTTPSanJoseStreamingSessionCreate: Invoked when an Adobe HDS (San Jose) session is created.

    • onHTTPSanJoseStreamingSessionDestroy: Invoked when a San Jose session is closed.

    IModuleOnHTTPMPEGDashStreamingSession


    public void onHTTPMPEGDashStreamingSessionCreate(
    	HTTPStreamerSessionMPEGDash httpMPEGDashStreamingSession);
    public void onHTTPMPEGDashStreamingSessionDestroy(
    	HTTPStreamerSessionMPEGDash httpMPEGDashStreamingSession);
    • onHTTPMPEGDASHStreamingSessionCreate: Invoked when an MPEG-DASH session is created.

    • onHTTPMPEGDASHStreamingSessionDestroy: Invoked when an MPEG-DASH session is closed.

    IModuleOnHTTPCupertinoEncryption


    public void onHTTPCupertinoEncryptionKeyRequest(
    HTTPStreamerSessionCupertino httpSession, IHTTPRequest req, 
    IHTTPResponse resp);
    public void onHTTPCupertinoEncryptionKeyCreateVOD(
    HTTPStreamerSessionCupertino httpSession, byte[] encKey);
    public void onHTTPCupertinoEncryptionKeyCreateLive(
    IApplicationInstance appInstance, String streamName, byte[] encKey);
    • onHTTPCupertinoEncryptionKeyRequest: Invoked when an encryption key request is made for Apple HLS (Cupertino) streaming.

    • onHTTPCupertinoEncryptionKeyCreateVOD: Invoked when an encryption key is created for a Cupertino video on demand stream.

    • onHTTPCupertinoEncryptionKeyCreateLive: Invoked when an encryption key is created for a Cupertino live stream.

    IModuleOnHTTPSmoothStreamingPlayReady


    public void onHTTPSmoothStreamingPlayReadyCreateVOD(
    HTTPStreamerSessionSmoothStreamer httpSession, 
    PlayReadyKeyInfo playReadyKeyInfo);
    public void onHTTPSmoothStreamingPlayReadyCreateLive(
    IApplicationInstance appInstance, String streamName, 
    PlayReadyKeyInfo playReadyKeyInfo);
    • onHTTPSmoothStreamingPlayReadyCreateVOD: Invoked when an encryption key request is made for Smooth Streaming video on demand.

    • onHTTPSmoothStreamingPlayReadyCreateLive: Invoked when an encryption key request is made for Smooth Streaming live.

    IModuleOnHTTPMPEGDashEncryption


    public void onHTTPMPEGDashEncryptionKeyVODChunk(
    HTTPStreamerSessionMPEGDash httpSession, 
    IHTTPStreamerMPEGDashIndex index, CencInfo cencInfo, long chunkId);
    public void onHTTPMPEGDashEncryptionKeyLiveChunk(
    ILiveStreamPacketizer liveStreamPacketizer, 
    String streamName, CencInfo cencInfo, long chunkId);
    • onHTTPMPEGDashEncryptionKeyVODChunk: Invoked when an encryption key request is made for MPEG-DASH video on demand.

    • onHTTPMPEGDashEncryptionKeyLiveChunk: Invoked when an encryption key request is made for MPEG-DASH live.

    Custom methods

    You can expose public custom methods to Adobe Flash Player through calls to the client-side interface NetConnection.call() or in calls that are part of the NetConnection or NetStream command set. For example, play and publish are defined in ModuleCode as custom methods. These methods must be public and must have the argument signature (IClient, RequestFunction, AMFDataList params). Only public methods with this signature are available to be called from Flash Player.

    Custom methods are processed differently than event methods. When a method is invoked from Flash Player, only the last module in the application's Modules list that defines that custom method is invoked. For example, the ModuleCore module defines the method play, which is invoked when NetStream.play(streamName) is called from Flash Player. If you create your own custom module that defines the method play and add it to the Modules list after the ModuleCore, then your play method is invoked instead of the play method that's defined in ModuleCore. In your implementation of play, if you want to invoke the play method of the module that precedes your module in the Modules list, you call this.invokePrevious(client, function, params). Wowza Streaming Engine software searches upwards in the Modules list, finds the next module that implements the play method, and invokes that method. This is similar to traditional object-oriented sub-classing. Each implementation of a method in the Modules list can perform an operation based on the invocation of a given method and can choose to pass control to the next module above them in the Modules list that implements the method.

    For example, assume that you want to check the stream name of calls made to NetStream.play(streamName) in your implementation of play. If the stream name starts with goodstream/, you want to append the phrase _good to the stream name and then call this.invokePrevious(client, function, params). All other connections are disconnected. The code looks like this:
    package com.mycompany.module;
    import com.wowza.wms.module.*;
    import com.wowza.wms.client.*;
    import com.wowza.wms.amf.*;
    import com.wowza.wms.request.*;
    
    public class MyModule extends ModuleBase
    {
    	public void play(IClient client, RequestFunction function, AMFDataList params)
    	{
    		boolean disconnect = false;
    		if (params.get(PARAM1).getType() == AMFData.DATA_TYPE_STRING)
    		{
    			String playName = params.getString(PARAM1);
    			if (playName.startsWith("goodstream/"))
    			{
    				playName += "_good";
    				params.set(PARAM1, new AMFDataItem(playName));
    			}
    			else
    				disconnect = true;
    		}
    		
    		if (disconnect)
    			client.setShutdownClient(true);
    		else
    			this.invokePrevious(client, function, params);
    	}
    }

    onCall methods

    The onCall method is a catch-all for methods that aren't defined by custom methods. The IModuleOnCall interface class defines the interface for this method. The onCall method works just like an event method in that all onCall methods that are defined in all modules are called. For example:
    package com.mycompany.module;
    
    import com.wowza.wms.module.*;
    import com.wowza.wms.client.*;
    import com.wowza.wms.amf.*;
    import com.wowza.wms.request.*;
    
    public class MyModule extends ModuleBase implements IModuleOnCall
    {
    	public void onCall(String handlerName, IClient client, 
    RequestFunction function, AMFDataList params)
    	{
    		getLogger().info("onCall: "+handlerName); 
    	}
    }

    Adobe Flash Player and custom methods

    Parameters passed from Adobe Flash Player to a Wowza Streaming Engine server must be marshaled to Java primitive and object types. The com.wowza.wms.module.ModuleBase class includes helper functions and constants for converting the parameter values. For more complex types, the com.wowza.wms.amf package contains an API for object conversion. See the Wowza Streaming Engine Server-Side API and the Server Side Coding example ([install-dir]/examples/ServerSideModules).

    The following example shows how to convert three incoming parameters:
    package com.mycompany.module;
    
    import com.wowza.wms.module.*;
    import com.wowza.wms.client.*;
    import com.wowza.wms.amf.*;
    import com.wowza.wms.request.*;
    
    public class MyModule extends ModuleBase
    {
    	public void myFunction(IClient client, 
    		RequestFunction function, AMFDataList params)
    	{
    		String param1 = getParamString(params, PARAM1); 
    		int param2 = getParamInt(params, PARAM2); 
    		boolean param3 = getParamBoolean(params, PARAM3);
    	}
    }
    A custom method called from Flash Player may return a single result value, which must be converted to an Action Message Format (AMF) object to be understood by Flash Player. These value types can include simple types like strings, integers, and Booleans and more complex types like objects, arrays, or arrays of objects. The com.wowza.wms.module.ModuleBase class includes helper functions for returning simple types. For more complex types, the com.wowza.wms.amf package has an API for creating and converting objects. See the Wowza Streaming Engine Server-Side API and the Server Side Coding example ([install-dir]/examples/ServerSideModules).

    The following example shows how to return simple value types from three methods:
    package com.mycompany.module;
    import com.wowza.wms.module.*;
    import com.wowza.wms.client.*;
    import com.wowza.wms.amf.*;
    import com.wowza.wms.request.*;
    
    public class MyModule extends ModuleBase
    {
    	public void myFunctionString(IClient client, 
    		RequestFunction function, AMFDataList params)
    	{
    		sendResult(client, params, "Hello World");
    	}
    
    	public void myFunctionInt(IClient client, 
    		RequestFunction function, AMFDataList params)
    	{
    		sendResult(client, params, 536);
    	}
    
    	public void myFunctionBoolean(IClient client, 
    		RequestFunction function, AMFDataList params)
    	{
    		sendResult(client, params, true);
    	}
    }

    Adobe Flash Player and server-to-client calls

    A custom method can call a function in Adobe Flash Player directly by invoking the IClient.call() method. The client call can return a single variable that is received by the server by creating a result object that implements the com.mycompany.module.IModuleCallResult interface. The IClient.call() method has two forms:
    public abstract void call(String handlerName);
    public abstract void call(String handlerName,
    	IModuleCallResult resultObj, Object ... params);
    Methods on the client-side are made available to the server by attaching them to the NetConnection object. The following is a sample ActionScript 3.0 client-side code:
    var nc:NetConnection = new NetConnection();
    var clientObj:Object = new Object();
    
    clientObj.serverToClientMethod = function(param1, param2)
    {
    	return "Hello World";
    }
    
    nc.client = clientObj; 
    nc.connect("rtmp://wms.mycompany.com/mymodules");
    To call this client-side method from the server, the custom method looks like this:
    package com.mycompany.module;
    
    import com.wowza.wms.module.*;
    import com.wowza.wms.client.*;
    import com.wowza.wms.amf.*;
    import com.wowza.wms.request.*;
    
    class MyResult implements IModuleCallResult
    {
    	public onResult(IClient client, 
    		RequestFunction function, AMFDataList params)
    {
    	String returnValue = getParamString(params, PARAM1);
    	getLogger().info("got Result: "+ returnValue);
    	}
    }
    
    public class MyModule extends ModuleBase
    {
    	public void myFunction(IClient client, 
    		RequestFunction function, AMFDataList params)
    	{
    		client.call("serverToClientMethod", new MyResult(),
    			"param1: value", 1.5);
    	}
    }

    Logging

    A custom method can get access to the server's logging interface using the getLogger() helper method that's implemented by the com.wowza.wms.module.ModuleBase base class. Log messages are written to the log files by using one of the following methods:
    getLogger().debug(logStr);
    getLogger().info(logStr);
    getLogger().warn(logStr);
    getLogger().error(logStr);

    HTTP provider classes


    HTTP Providers are Java classes that are mini Java servlets that can be used to add an HTTP interface to Wowza Streaming Engine software. They are configured on a per-port basis in [install-dir]/conf/VHost.xml. (See How to use server-side modules and HTTP Providers.) The following example shows a simple HTTP Provider that returns the server version:
    package com.mycompany.wms.http;
    import java.io.*;
    
    import com.wowza.wms.server.*;
    import com.wowza.wms.stream.*;
    import com.wowza.wms.vhost.*;
    import com.wowza.wms.http.*;
    
    public class HTTPServerVersion extends HTTPProvider2Base
    {
    	public void onHTTPRequest(IVHost vhost, IHTTPRequest req, IHTTPResponse resp)
    	{
    		if (!doHTTPAuthentication(vhost, req, resp))
    			return;
    		
    		String version = MediaStreamBase.p+" ";
    		version += ReleaseInfo.getVersion();
    		version += " build"+ReleaseInfo.getBuildNumber();
    		
    		String retStr = "<html><head><title>";
    		retStr += version;
    		retStr += "</title></head><body>"+version+"</body></html>";
    		try
    		{
    			OutputStream out = resp.getOutputStream();
    			byte[] outBytes = retStr.getBytes();
    			out.write(outBytes);
    		}
    		catch (Exception e)
    		{
    			System.out.println("HTMLServerVersion: "+e.toString());
    		}
    		
    	}
    }
    Much of the functionality of HTTP Providers is encapsulated in the HTTPProvider2Base base class. Your HTTP Provider, if it extends this class, only needs to implement the onHTTPRequest method. The following are some interesting code snippets to aid in HTTP Provider development:

    Get HTTP request URL
    String path = super.getPath(req, false);
    Get HTTP request header value
    String headerValue = req.getHeader(headerName);
    Set HTTP response header value
    resp.setHeader(headerName, headerValue);
    Set HTTP response status
    resp.setResponseCode(404);
    More complex and interesting HTTP Provider examples can be found in the HTTP Providers articles.

    Event listeners


    You can add event listeners to many points in the Wowza Streaming Engine object hierarchy. Event listeners are classes that implement a notifier interface and are notified of specific events within the server. For example, you can inject a server listener that gets notified of server startup, initialization, and shutdown or an application instance listener that gets notified with an application instance is started or stopped. See How to use event listeners.

    Originally Published: For Wowza Streaming Engine on 11-20-2015.

    If you're having problems or want to discuss this article, post in our forum.