Resolve SMIL file requests with the Wowza Streaming Engine Java API

SMIL files are used in Wowza Streaming Engine™ to describe multi-bitrate streams, both live and video-on-demand. You can use the Wowza Streaming Engine Java API to intercept requests for multi-bitrate streams and provide the stream grouping. To use this feature, you must use the stream name prefix amlst: (which stands for API-based MediaList). A MediaList is a set of Java objects that describe a multi-bitrate stream. When Wowza Streaming Engine reads a SMIL file, it creates a MediaList and passes it back to the streaming provider. The Java API provides a means to intercept these requests and create the MediaList dynamically in a Wowza Streaming Engine module.

Each ApplicationInstance has a IMediaListProvider provider interface. This interface is used to resolve stream names that are prefixed with the amlst: prefix to MediaList objects. In a module you can create your own IMediaListProvider implementation and replace the default implementation.

Note: For examples of intercepting requests for multi-bitrate streams that include captions, see Resolve SMIL file requests with captions with the Wowza Streaming Engine Java API.

MediaList object structure


A MediaList has the following object structure:

MediaList
|
--MediaListSegment
  |
  --MediaListRendition
    [...]

A MediaList has a single MediaListSegment. The MediaListSegement can have multiple MediaListRenditions each representing a single multi-bitrate rendition.

Live streaming sample module


Here is a sample module that implements this interface for live streaming:

package com.wowza.wms.plugin.test.module;

import com.wowza.wms.medialist.*;
import com.wowza.wms.module.*;
import com.wowza.wms.stream.*;
import com.wowza.wms.application.*;

public class ModuleAMLSTTestLive extends ModuleBase 
{
	class MyMediaListProvider implements IMediaListProvider
	{
		public MediaList resolveMediaList(IMediaListReader mediaListReader, IMediaStream stream, String streamName)
		{
			MediaList mediaList = new MediaList();

			MediaListSegment segment = new MediaListSegment();
			mediaList.addSegment(segment);

			MediaListRendition rendition1 = new MediaListRendition();
			segment.addRendition(rendition1);

			rendition1.setName(streamName+"_400");
			rendition1.setBitrateAudio(128000);
			rendition1.setBitrateVideo(400000);
			rendition1.setWidth(320);
			rendition1.setHeight(240);
			rendition1.setAudioCodecId("mp4a.40.2");
			rendition1.setVideoCodecId("avc1.66.12");

			MediaListRendition rendition2 = new MediaListRendition();
			segment.addRendition(rendition2);

			rendition2.setName(streamName+"_800");
			rendition2.setBitrateAudio(128000);
			rendition2.setBitrateVideo(800000);
			rendition2.setWidth(640);
			rendition2.setHeight(480);
			rendition2.setAudioCodecId("mp4a.40.2");
			rendition2.setVideoCodecId("avc1.77.31");

			return mediaList;
		}
	}

	public void onAppStart(IApplicationInstance appInstance)
	{
		appInstance.setMediaListProvider(new MyMediaListProvider());
	}

}

Add this module as the last entry in the <Modules> list in [install-dir]/conf/[application]/Application.xml:

<Module>
	<Name>ModuleAMLSTTestLive</Name>
	<Description>ModuleAMLSTTestLive</Description>
	<Class>com.wowza.wms.plugin.test.module.ModuleAMLSTTestLive</Class>
</Module>

Quickly looking at the components of this module:

  • Module extends the ModuleBase
  • Internal class MyMediaListProvider implements the IMediaListProvider interface and returns a new MediaList based on the stream name.
  • The onAppStart method is used to replace the application instance level IMediaListProvider with our implementation.

This module will intercept any request for stream names that start with amlst: such as

http://[wowza-ip-address]:1935/[application-name]/amlst:myStream/playlist.m3u8

It will return a multi-bitrate description with two renditions using the stream name as the base name for the filename. This is equivalent to the SMIL file:

<smil>
	<head>
	</head>
	<body>
		<switch>
			<video src="myStream_400" width="320" height="240" system-bitrate="528000">
				<param name="audioCodecId" value="mp4a.40.2" valuetype="data"/>
				<param name="videoCodecId" value="avc1.66.12" valuetype="data"/>
				<param name="audioBitrate" value="128000" valuetype="data"/>
				<param name="videoBitrate" value="400000" valuetype="data"/>
			</video>
			<video src="myStream_800" width="640" height="480" system-bitrate="928000">
				<param name="audioCodecId" value="mp4a.40.2" valuetype="data"/>
				<param name="videoCodecId" value="avc1.77.31" valuetype="data"/>
				<param name="audioBitrate" value="128000" valuetype="data"/>
				<param name="videoBitrate" value="800000" valuetype="data"/>
			</video>
		</switch>
	</body>
</smil>

Video-on-demand streaming sample module


Here is a sample module that implements this interface for VOD streaming::

package com.wowza.wms.plugin.test.module;

import com.wowza.wms.medialist.*;
import com.wowza.wms.module.*;
import com.wowza.wms.stream.*;
import com.wowza.wms.application.*;

public class ModuleAMLSTTest extends ModuleBase 
{
	class MyMediaListProvider implements IMediaListProvider
	{
		public MediaList resolveMediaList(IMediaListReader mediaListReader, IMediaStream stream, String streamName)
		{
			MediaList mediaList = new MediaList();

			MediaListSegment segment = new MediaListSegment();
			mediaList.addSegment(segment);

			MediaListRendition rendition1 = new MediaListRendition();
			segment.addRendition(rendition1);

			rendition1.setName("mp4:"+streamName+"_400k.mp4");
			rendition1.setBitrateAudio(128000);
			rendition1.setBitrateVideo(400000);
			rendition1.setWidth(320);
			rendition1.setHeight(240);
			rendition1.setAudioCodecId("mp4a.40.2");
			rendition1.setVideoCodecId("avc1.66.12");

			MediaListRendition rendition2 = new MediaListRendition();
			segment.addRendition(rendition2);

			rendition2.setName("mp4:"+streamName+"_800k.mp4");
			rendition2.setBitrateAudio(128000);
			rendition2.setBitrateVideo(800000);
			rendition2.setWidth(640);
			rendition2.setHeight(480);
			rendition2.setAudioCodecId("mp4a.40.2");
			rendition2.setVideoCodecId("avc1.77.31");

			return mediaList;
		}
	}

	public void onAppStart(IApplicationInstance appInstance)
	{
		appInstance.setMediaListProvider(new MyMediaListProvider());
	}

}

Add this module as the last entry in the <Modules> list in [install-dir]/conf/[application]/Application.xml:

<Module>
	<Name>ModuleAMLSTTest</Name>
	<Description>ModuleAMLSTTest</Description>
	<Class>com.wowza.wms.plugin.test.module.ModuleAMLSTTest</Class>
</Module>

These are the components of this module:

  • Module extends the ModuleBase
  • Internal class MyMediaListProvider implements the IMediaListProvider interface and returns a new MediaList based on the stream name.
  • The onAppStart method is used to replace the application instance level IMediaListProvider with our implementation.

This module will intercept any request for stream names that start with amlst: such as:

http://[wowza-ip-address]:1935/[application-name]/amlst:sample/playlist.m3u8

It will return a multi-bitrate description with two renditions using the stream name as the base name for the filename. This is equivalent to the SMIL file:

<smil>
	<head>
	</head>
	<body>
		<switch>
			<video src="mp4:sample_400.mp4" width="320" height="240" system-bitrate="528000">
				<param name="videoCodecId" value="avc1.66.12" valuetype="data"/>
				<param name="audioCodecId" value="mp4a.40.2" valuetype="data"/>
				<param name="audioBitrate" value="128000" valuetype="data"/>
				<param name="videoBitrate" value="400000" valuetype="data"/>
			</video>
			<video src="mp4:sample_800.mp4" width="640" height="480" system-bitrate="928000">
				<param name="videoCodecId" value="avc1.77.31" valuetype="data"/>
				<param name="audioCodecId" value="mp4a.40.2" valuetype="data"/>
				<param name="audioBitrate" value="128000" valuetype="data"/>
				<param name="videoBitrate" value="800000" valuetype="data"/>
			</video>
		</switch>
	</body>
</smil>
 
Note: The IMediaListReader interface does provide additional context information such as the IHTTPStreamerSession from which the stream is being requested.

Low-Latency HLS live streaming sample module


Here is a sample module that implements this interface for Low-Latency HLS live streaming:

package com.wowza.wms.plugin.test.module;

import com.wowza.wms.medialist.*;
import com.wowza.wms.module.*;
import com.wowza.wms.stream.*;
import com.wowza.wms.application.*;

public class CmafAmlst extends ModuleBase 
{
	class CmafAmlstProvider implements IMediaListProvider
	{
		public MediaList resolveMediaList(IMediaListReader mediaListReader, IMediaStream stream, String streamName)
		{
			MediaList mediaList = new MediaList();

			MediaListSegment segment = new MediaListSegment();
			mediaList.addSegment(segment);

			MediaListRendition rendition1 = new MediaListRendition();
			segment.addRendition(rendition1);

			rendition1.setName(streamName+"_160p");
			rendition1.setBitrateAudio(100000);
			rendition1.setWowzaAudioOnly(true);
			rendition1.setTitle("English");
			rendition1.setLanguage("eng");
			WMSProperties props1 = rendition1.getProperties(true);
				props1.setProperty("cupertinoTag", "EXT-X-MEDIA");
				props1.setProperty("cupertinoTag.GROUP-ID", "aac");
				props1.setProperty("cupertinoTag.DEFAULT", "FALSE");
				props1.setProperty("cupertinoTag.AUTOSELECT", "YES");
			
			MediaListRendition rendition2 = new MediaListRendition();
			segment.addRendition(rendition2);

			rendition2.setName(streamName+"_source");
			rendition2.setBitrateVideo(1300000);
			rendition2.setWidth(960);
			rendition2.setHeight(540);
			rendition2.setAudioCodecId("mp4a.40.2");
			rendition2.setVideoCodecId("avc1.77.32");
			rendition2.setWowzaVideoOnly(true);
			WMSProperties props2 = rendition2.getProperties(true);
				props2.setProperty("cupertinoTag.AUDIO", "aac");

			MediaListRendition rendition3 = new MediaListRendition();
			segment.addRendition(rendition3);

			rendition3.setName(streamName+"_360p");
			rendition3.setBitrateVideo(850000);
			rendition3.setWidth(640);
			rendition3.setHeight(360);
			rendition3.setAudioCodecId("mp4a.40.2");
			rendition3.setVideoCodecId("avc1.77.31");
			rendition3.setWowzaVideoOnly(true);
			WMSProperties props3 = rendition3.getProperties(true);
				props3.setProperty("cupertinoTag.AUDIO", "aac");
			
			MediaListRendition rendition4 = new MediaListRendition();
			segment.addRendition(rendition4);

			rendition4.setName(streamName+"_160p");
			rendition4.setBitrateVideo(200000);
			rendition4.setWidth(284);
			rendition4.setHeight(160);
			rendition4.setAudioCodecId("mp4a.40.2");
			rendition4.setVideoCodecId("avc1.66.21");
			rendition4.setWowzaVideoOnly(true);
			WMSProperties props4 = rendition4.getProperties(true);
				props4.setProperty("cupertinoTag.AUDIO", "aac");

			return mediaList;
		}
	}

	public void onAppStart(IApplicationInstance appInstance)
	{
		appInstance.setMediaListProvider(new CmafAmlstProvider());
	}

}
 
Note: SMIL files for Low-Latency HLS streaming require specific attributes and parameters. For more information, see Create adaptive bitrate CMAF streams using Wowza Streaming Engine.

Add this module as the last entry in the <Modules> list in [install-dir]/conf/[application]/Application.xml:

<Module>
	<Name>CmafAmlst</Name>
	<Description>CmafAmlst</Description>
	<Class>com.wowza.wms.plugin.test.module.CmafAmlst</Class>
</Module>

Quickly looking at the components of this module:

  • Module extends the ModuleBase
  • Internal class CmafAmlstProvider implements the IMediaListProvider interface and returns a new MediaList based on the stream name.
  • The onAppStart method is used to replace the application instance level IMediaListProvider with our implementation.

This module will intercept any request for stream names that start with amlst: such as

http://[wowza-ip-address]:1935/[application-name]/amlst:myStream/playlist.m3u8

It will return a multi-bitrate description with three video renditions and one audio rendition using the stream name as the base name for the filename. This is equivalent to the SMIL file:

<smil>
	<head>
	</head>
	<body>
		<switch>
			<video src="mp4:myStream_160p" system-language="en" title="English" audio-bitrate="100000">
			   <param name="audioOnly" value="TRUE" valuetype="data"/>
			   <param name="cupertinoTag" value="EXT-X-MEDIA" valuetype="data"/>
                           <param name="cupertinoTag.GROUP-ID" value="aac" valuetype="data"/>
                           <param name="cupertinoTag.DEFAULT" value="YES" valuetype="data"/>
                           <param name="cupertinoTag.AUTOSELECT" value="YES" valuetype="data"/>
			</video>
			<video src="mp4:myStream_source" width="960" height="540" video-bitrate="1300000">
                           <param name="videoCodecId" value="avc1.77.32" valuetype="data"/>
                           <param name="videoOnly" value="TRUE" valuetype="data"/>
                           <param name="cupertinoTag.AUDIO" value="aac" valuetype="data"/>
                           <param name="audioCodecId" value="mp4a.40.2" valuetype="data"/>
			</video>
			<video src="mp4:myStream_360p" width="640" height="360" video-bitrate="850000">
                           <param name="videoCodecId" value="avc1.77.31" valuetype="data"/>
                           <param name="videoOnly" value="TRUE" valuetype="data"/>
                           <param name="cupertinoTag.AUDIO" value="aac" valuetype="data"/>
                           <param name="audioCodecId" value="mp4a.40.2" valuetype="data"/>
			</video>
			<video src="mp4:myStream_160p" width="284" height="160" video-bitrate="200000">
                           <param name="videoCodecId" value="avc1.66.21" valuetype="data"/>
                           <param name="videoOnly" value="TRUE" valuetype="data"/>
                           <param name="cupertinoTag.AUDIO" value="aac" valuetype="data"/>
                           <param name="audioCodecId" value="mp4a.40.2" valuetype="data"/>
			</video>
		</switch>
	</body>
</smil>