Resolve SMIL file requests with captions with the Wowza Streaming Engine Java API

Learn how to intercept requests for multi-bitrate streams that include captions and programmatically provide the stream grouping needed for adaptive bitrate streaming using the Wowza Streaming Engine™ Java API. Rather than creating a SMIL file to describe the stream grouping and associated captions, you can use one of the following sample modules, and Wowza Streaming Engine automatically creates a MediaList to describe the stream grouping anytime the server receives a stream request that includes the amlst stream name prefix.

Notes: 

Live stream with WebVTT captions sample module


The following example implements the IMediaListProvider provider interface to resolve an amlst request for a live stream that includes Web Video Text Tracks (WebVTT) captions converted from embedded Action Message Format (AMF) onTextData. This example assumes your source stream has AMF onTextData events injected using the ModulePublishOnTextData example Java module and that your application is configured to convert the captions for WebVTT output as described in Configure WebVTT captions for Wowza Streaming Engine iOS streams.

Note: You can download a package that includes the ModulePublishOnTextData example module that injects onTextData events into a live stream: Download PublishOnTextData.zip. This module takes caption data from a flat text file and injects an onTextData event into the live stream every 6.5 seconds. This module is only for testing purposes and is an example of how to inject onTextData events into a live stream.
  1. To use the following custom module, compile it, package it into .jar file, and put it in the [install-dir]/lib folder.

    Overview of the components of this module:

    • Module extends the ModuleBase.
    • ModuleAMLSTWebVTT 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.
     
    package com.wowza.wms.plugin.test2;
    
    import com.wowza.wms.application.IApplicationInstance;
    import com.wowza.wms.application.WMSProperties;
    import com.wowza.wms.medialist.MediaList;
    import com.wowza.wms.medialist.MediaListRendition;
    import com.wowza.wms.medialist.MediaListSegment;
    import com.wowza.wms.module.ModuleBase;
    import com.wowza.wms.stream.IMediaListProvider;
    import com.wowza.wms.stream.IMediaListReader;
    import com.wowza.wms.stream.IMediaStream;
    import com.wowza.wms.vhost.IVHost;
    
    public class ModuleAMLSTWebVTT extends ModuleBase implements IMediaListProvider
    {
    	@Override
    	public MediaList resolveMediaList(IMediaListReader mediaListReader, IMediaStream stream, String streamName)
    	{
    		getLogger().info("ModuleAMLSTWebVTT:resolveMediaList called!");
    		
    		//Video rendition created from an RTMP input stream. The codec and display information here matches that of the sample.mp4 file installed with Wowza Streaming Engine.
    		MediaListRendition videoAndAudioRendition = new MediaListRendition();
    		videoAndAudioRendition.setName(streamName);
    		videoAndAudioRendition.setBitrateTotal(761680);
    		videoAndAudioRendition.setWidth(512);
    		videoAndAudioRendition.setHeight(288);
    		videoAndAudioRendition.setAudioCodecId("mp4a.40.2");
    		videoAndAudioRendition.setVideoCodecId("avc1.42c015");
    		
    		//Subtitle rendition created with embedded AMF onTextData events injected by the ModulePublishOnTextData example module.
    		MediaListRendition modulePublishOnTextDataInputStream = new MediaListRendition();
    		modulePublishOnTextDataInputStream.setName(streamName);
    		modulePublishOnTextDataInputStream.setType(IVHost.CONTENTTYPE_DATA);
    		modulePublishOnTextDataInputStream.setLanguage("eng,fra");
    		WMSProperties props = modulePublishOnTextDataInputStream.getProperties(true);
                    props.setProperty("iswowzacaptionstream", true);
                    props.setProperty("wowzaCaptionIngestType", true);
            
                    //Add the renditions to the MediaListSegment.
    		MediaListSegment segment = new MediaListSegment();
    		segment.addRendition(videoAndAudioRendition);
    		segment.addRendition(modulePublishOnTextDataInputStream);
    		
    		//Add the segment to the MediaList.
    		MediaList mediaList = new MediaList();
    		mediaList.addSegment(segment);
    		
                    //Access logs report the SMIL file equivalent of the MediaList to confirm if your MediaList configuration and stream grouping is accurate.
    		getLogger().info("ModuleAMLSTWebVTT:resolveMediaList - SMIL file conversion:\n" + mediaList.toSMILString());
    		return mediaList;
    	}
    
    	public void onAppStart(IApplicationInstance appInstance)
    	{
    		//Register the MediaList resolver.
    		getLogger().info("onAppStart[\""+appInstance.getApplication().getName()+"/"+appInstance.getName() + "\"]! Setting ModuleAMLSTWebVTT to resolve stream names");
    		appInstance.setMediaListProvider(new ModuleAMLSTWebVTT());
    	}
    
    }
    
     
    Note: The IMediaListReader interface does provide additional context information such as the IHTTPStreamerSession from which the stream is being requested.
  2. Add the module as the last entry in the <Modules> list in the Application.xml file for your live application:
     
    <Module>
    	<Name>ModuleAMLSTTestLiveWebVTT</Name>
    	<Description>ModuleAMLSTTestLiveWebVTT</Description>
    	<Class>com.wowza.wms.plugin.test2.ModuleAMLSTWebVTT</Class>
    </Module>
  3. To confirm your module is creating the MediaList stream grouping needed, check the wowzastreamingengine_access.log for the SMIL file equivalent to the MediaList that the module returns. For example:
     
    INFO 200 - ModuleAMLSTWebVTT:resolveMediaList - SMIL file conversion: <smil><head></head><body> <switch> <video src="myStream" system-bitrate="761680" width="512" height="288"> <param name="videoCodecId" value="avc1.42c015" valuetype="data"/> <param name="audioCodecId" value="mp4a.40.2" valuetype="data"/> </video> <textstream src="myStream" system-language="eng,fra"> <param name="iswowzacaptionstream" value="true"/> <param name="wowzaCaptionIngestType" value="true"/> </textstream> </switch></body></smil>
    

This module intercepts any request for streams that include the amlst prefix, such as:

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

The module returns a MediaList stream grouping with video renditions and captions that is equivalent to using the following SMIL file:

<smil>
  <head>
  </head>
  <body>
    <switch>
	  <video src="myStream" system-bitrate="761680" width="512" height="288">
	    <param name="videoCodecId" value="avc1.42c015" valuetype="data"/>
	    <param name="audioCodecId" value="mp4a.40.2" valuetype="data"/>
	  </video>
	  <textstream src="myStream" system-language="eng,fra">
	    <param name="iswowzacaptionstream" value="true"/>
            <param name="wowzaCaptionIngestType" value="true"/>
      </textstream>
    </switch>
  </body>
</smil>

VOD stream with TTML captions sample module


The following example implements the IMediaListProvider provider interface to resolve an amlst request for a VOD stream that includes Timed Text Markup Language (TTML) captions. This example assumes your application is configured for using TTML captions as described in Configure closed captioning for Wowza Streaming Engine video-on-demand streams.

  1. To use the following custom module, compile it, package it into .jar file, and put it in the [install-dir]/lib folder.

    Overview of the components of this module:

    • Module extends the ModuleBase.
    • ModuleAMLSTTTML 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.
     
    package com.wowza.wms.plugin.test2;
    
    import com.wowza.wms.application.IApplicationInstance;
    import com.wowza.wms.application.WMSProperties;
    import com.wowza.wms.medialist.MediaList;
    import com.wowza.wms.medialist.MediaListRendition;
    import com.wowza.wms.medialist.MediaListSegment;
    import com.wowza.wms.module.ModuleBase;
    import com.wowza.wms.stream.IMediaListProvider;
    import com.wowza.wms.stream.IMediaListReader;
    import com.wowza.wms.stream.IMediaStream;
    import com.wowza.wms.vhost.IVHost;
    
    public class ModuleAMLSTTTML extends ModuleBase implements IMediaListProvider
    {
    	@Override
    	public MediaList resolveMediaList(IMediaListReader mediaListReader, IMediaStream stream, String streamName)
    	{
    		getLogger().info("ModuleAMLSTTTML:resolveMediaList called!");
    		
    		//Video rendition created from the sample.mp4 file installed with Wowza Streaming Engine.
    		String vodMediaAsset = "sample.mp4";
    		MediaListRendition videoAndAudioRendition = new MediaListRendition();
    		videoAndAudioRendition.setName(vodMediaAsset);
    		videoAndAudioRendition.setBitrateTotal(761680);
    		videoAndAudioRendition.setWidth(512);
    		videoAndAudioRendition.setHeight(288);
    		videoAndAudioRendition.setAudioCodecId("mp4a.40.2");
    		videoAndAudioRendition.setVideoCodecId("avc1.42c015");
    		
    		//Subtitle rendition created from a TTML file (sample.ttml).
    		String vodTTMLAsset = "sample.ttml";
    		MediaListRendition captionRendtion = new MediaListRendition();
    		captionRendtion.setName(vodTTMLAsset);
    		captionRendtion.setType(IVHost.CONTENTTYPE_DATA);
    		captionRendtion.setLanguage("eng");
    		WMSProperties props = captionRendtion.getProperties(true);
                    props.setProperty("iswowzacaptionstream", true);
            
                    //Add the renditions to the MediaListSegment.
    		MediaListSegment segment = new MediaListSegment();
    		segment.addRendition(videoAndAudioRendition);
    		segment.addRendition(captionRendtion);
    		
    		//Add the segment to the MediaList.
    		MediaList mediaList = new MediaList();
    		mediaList.addSegment(segment);
    		
                    //Access logs report the SMIL file equivalent of the MediaList to confirm if your MediaList configuration and stream grouping is accurate.
    		getLogger().info("ModuleAMLSTTTML:resolveMediaList - SMIL file conversion:\n" + mediaList.toSMILString());
    		return mediaList;
    	}
    
    	public void onAppStart(IApplicationInstance appInstance)
    	{
    		//Register the MediaList resolver.
    		getLogger().info("onAppStart[\""+appInstance.getApplication().getName()+"/"+appInstance.getName() + "\"]! Setting ModuleAMLSTTTML to resolve stream names");
    		appInstance.setMediaListProvider(new ModuleAMLSTTTML());
    	}
    
    }
    
     
    Note: The IMediaListReader interface does provide additional context information such as the IHTTPStreamerSession from which the stream is being requested.
  2. Add the module as the last entry in the <Modules> list in the Application.xml file for your VOD application:
     
    <Module>
    	<Name>ModuleAMLSTTestVODTTML</Name>
    	<Description>ModuleAMLSTTestVODTTML</Description>
    	<Class>com.wowza.wms.plugin.test2.ModuleAMLSTTTML</Class>
    </Module>
  3. To confirm your module is creating the MediaList stream grouping needed, check the wowzastreamingengine_access.log for the SMIL file equivalent to the MediaList that the module returns. For example:
     
    INFO 200 - ModuleAMLSTWebVTT:resolveMediaList - SMIL file conversion: <smil><head></head><body> <switch> <video src="sample.mp4" system-bitrate="761680" width="512" height="288"> <param name="videoCodecId" value="avc1.42c015" valuetype="data"/> <param name="audioCodecId" value="mp4a.40.2" valuetype="data"/> </video> <textstream src="sample.ttml" system-language="eng"> <param name="iswowzacaptionstream" value="true"/> </textstream> </switch></body></smil>
    

This module intercepts any request for streams that include the amlst prefix, such as:

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

The module returns a MediaList stream grouping with video renditions and captions that is equivalent to using the following SMIL file:

<smil>
  <head>
  </head>
  <body>
    <switch>
	  <video src="sample.mp4" system-bitrate="761680" width="512" height="288">
            <param name="videoCodecId" value="avc1.42c015" valuetype="data"/>
	    <param name="audioCodecId" value="mp4a.40.2" valuetype="data"/>
	  </video>
	  <textstream src="sample.ttml" system-language="eng">
	    <param name="iswowzacaptionstream" value="true"/>
	  </textstream>
    </switch>
  </body>
</smil>
 

More resources