Extract MP4-formatted files from DVR streams with the Wowza Streaming Engine Java API

This article provides an example to show you how to use the Wowza Streaming Engine Java API to convert nDVR stores into MP4 files for playback as on-demand files. The examples show how to identify available nDVR stores for conversion, how to set a starting point and duration for clip extraction, and how to query running conversions.

Note: Wowza Streaming Engine™ 4.4.0 or later is required.

Compiling the converter HTTP provider


Compile the following converter HTTP provider example with the Wowza IDE.

package com.wowza.demo.ndvrconverter;

import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;

import com.wowza.wms.application.IApplication;
import com.wowza.wms.application.IApplicationInstance;
import com.wowza.wms.dvr.DvrApplicationConverterContext;
import com.wowza.wms.dvr.converter.DvrConverterControlBase;
import com.wowza.wms.dvr.converter.DvrConverterStatus;
import com.wowza.wms.dvr.converter.IDvrConverter;
import com.wowza.wms.dvr.converter.IDvrConverterStore;
import com.wowza.wms.http.*;
import com.wowza.wms.logging.*;
import com.wowza.wms.vhost.*;

public class HTTPConverter extends HTTPProvider2Base
{

	public void onHTTPRequest(IVHost vhost, IHTTPRequest req, IHTTPResponse resp)
	{
		if (!doHTTPAuthentication(vhost, req, resp))
			return;

		String retStr = "";
		
		Map<String, List<String>> params = req.getParameterMap();
		String startOffSet = "";
		String endOffSet = "";
		String appName = "live";
		String appInstanceName = "_definst_";
		String streamName = "";
		String command = "status";
		
		long startOffSetVal = 0;
		long endOffSetVal = 0;
		
		if ( params.containsKey("startoffset") )
			startOffSet = params.get("startoffset").get(0);
		
		if ( params.containsKey("endoffset") )
			endOffSet = params.get("endoffset").get(0);

		if ( params.containsKey("appname") )
			appName = params.get("appname").get(0);

		if ( params.containsKey("instancename") )
			appInstanceName = params.get("instancename").get(0);

		if ( params.containsKey("command") )
			command = params.get("command").get(0);
		
		if ( params.containsKey("streamname") )
			streamName = params.get("streamname").get(0);
		
		try {
		if ( startOffSet.length()>0 )
			startOffSetVal = Long.valueOf(startOffSet);
		} catch ( Exception noNumber) { startOffSetVal = 0; }

		try {
		if ( endOffSet.length()>0 )
			endOffSetVal = Long.valueOf(endOffSet);
		} catch ( Exception noNumber) { endOffSetVal = 0; }
 
			
		IApplication app = vhost.getApplication(appName);
		if (app != null)
			{
			IApplicationInstance appInstance = app.getAppInstance(appInstanceName);
			if (appInstance != null)
				{ 				
				
					retStr+="Application name : "+app.getName()+"<br />";
					retStr+="Application Instance name : "+appInstance.getName()+"<br />";

					DvrApplicationConverterContext myConverterContext = appInstance.getDvrConverter();
				
					retStr+="DVR Context Object : "+myConverterContext+"<br />";
				
					SortedMap<String, SortedSet<Integer>> dvrStores = myConverterContext.getDvrStoresAvailable();
				
					Iterator<String> mainStoresI = dvrStores.keySet().iterator();
					while (mainStoresI.hasNext())
					{
						String storeName = mainStoresI.next();
						Iterator<Integer> sortedVersionsI = dvrStores.get(storeName).iterator();
						while (sortedVersionsI.hasNext())
						{
							Integer thisVersion = sortedVersionsI.next();
							retStr+="DVR Store Name : "+storeName+" contains version "+thisVersion+"<br />";
						}
					}
				
					if ( command.length()>0 && streamName.length()>0 )
					{

					if ( command.equalsIgnoreCase("convert") )
					{
						retStr+="DVR Store Conversion Selected <br />";

						String dvrStoreName = streamName + "." + dvrStores.get(streamName).first();
				
						retStr+="DVR Store Name To Use : "+dvrStoreName+"<br />";

						IDvrConverterStore dvrStore = myConverterContext.getDvrConverterStore(dvrStoreName);

						if ( dvrStore!=null )
						{

							long startClip = dvrStore.getUtcStart();
							long endClip =dvrStore.getUtcEnd();
							retStr+="DVR Store Name To Use : "+dvrStoreName+" start UTC : "+startClip+" <br />";
							retStr+="DVR Store Name To Use : "+dvrStoreName+"   end UTC : "+endClip+" <br />";
							
							retStr+="DVR Store Name To Use : "+dvrStoreName+" start offset : "+startOffSetVal+" <br />";
							retStr+="DVR Store Name To Use : "+dvrStoreName+"   end offset : "+endOffSetVal+" <br />";
							
							DvrConverterControlBase converterControl = new DvrConverterControlBase(appInstance);
							converterControl.setStartTime(startClip+startOffSetVal);
							converterControl.setEndTime(endClip-endOffSetVal);
							converterControl.setDebug(true);
							IDvrConverter converter = myConverterContext.startConversion(dvrStore,converterControl);
							String outputFilename = converter.getStatus().getFileName();
							retStr+="DVR Conversion started : "+dvrStoreName+"   Output filename : "+outputFilename+" <br />";
						}
										
					}
					else if ( command.equalsIgnoreCase("status") )
					{
						retStr+="DVR Store Status Selected <br />";

						String dvrStoreName = streamName + "." + dvrStores.get(streamName).first();
				
						retStr+="DVR Store Name To Use : "+dvrStoreName+"<br />";

						IDvrConverterStore dvrStore = myConverterContext.getDvrConverterStore(dvrStoreName);
					
						if ( dvrStore!=null )
						{
							DvrConverterStatus storeStatus = dvrStore.getConversionStatus();
							retStr+="State is : "+storeStatus.getState()+" <br />";
						}
					
					}
					else if ( command.equalsIgnoreCase("expire") )
					{
						retStr+="DVR Converter - Force Expire Previous Conversions <br />";
						Map<String,IDvrConverter> currentConversions = myConverterContext.getDvrConversions(false);
						
						Iterator<String> currentConversionsI = currentConversions.keySet().iterator();
						while (currentConversionsI.hasNext())
						{
							String thisStoreName = currentConversionsI.next();
							IDvrConverter thisStore = currentConversions.get(thisStoreName);
							retStr+="DVR Converter - Force Expire - Current Store : "+thisStoreName+" State : "+thisStore.getStatus().getState()+"<br />";
						}
						
						Map<String,IDvrConverter> unExpiredConversions = myConverterContext.getDvrConversions(true);

						retStr+="DVR Converter - Forcing Expiry <br />";

						Iterator<String> unExpiredConversionsI = unExpiredConversions.keySet().iterator();
						while (unExpiredConversionsI.hasNext())
						{
							String thisStoreName = unExpiredConversionsI.next();
							IDvrConverter thisStore = unExpiredConversions.get(thisStoreName);
							retStr+="DVR Converter - Force Expire - Unexpired Store : "+thisStoreName+" State : "+thisStore.getStatus().getState()+"<br />";
						}
					}

				}
				else
				{
					retStr += "Command and/or Stream Name not provided";
				}
			}
			else
			{
				retStr += "Application Instance is not available";
			}
		}
		else
		{
			retStr += "Application is not available";
		}


		try
		{
			OutputStream out = resp.getOutputStream();
			byte[] outBytes = retStr.getBytes();
			out.write(outBytes);
		}
		catch (Exception e)
		{
			WMSLoggerFactory.getLogger(null).error("HTTPConverter: " + e.toString());
		}

	}

}

To enable your compiled HTTP provider in Wowza Streaming Engine software, open the [install-dir]/conf/VHost.xml file in a text editor and add the converter HTTP provider information as shown below (Important: Be sure to add the converter HTTP provider before the ServerInfo HTTP provider as shown in the example):

<HTTPProvider>
	<BaseClass>com.wowza.demo.ndvrconverter.HTTPConverter</BaseClass>
	<RequestFilters>converter*</RequestFilters>
	<AuthenticationMethod>none</AuthenticationMethod>
</HTTPProvider>
<HTTPProvider>
	<BaseClass>com.wowza.wms.http.HTTPServerInfoXML</BaseClass>
	<RequestFilters>serverinfo*</RequestFilters>
	<AuthenticationMethod>admin-digest</AuthenticationMethod>
</HTTPProvider>

Live to VOD clip extraction examples


The base URL used to query the Converter is:

http://[wowza-ip-address]:1935/converter

Where [wowza-ip-address] is the IP address of the Wowza media server that has the nDVR stores.

You can attach the following parameters to the base URL query to perform specific functions:

  • startoffset - Specifies when to start clip extraction and is specified as an offset value (in milliseconds) from the start of the nDVR store. The default value (0) means clip extraction starts at the beginning of the nDVR store.
     
  • endoffset - Specifies when to stop clip extraction and is specified as an offset value (in milliseconds) from the end of the nDVR store. The default value (0) means clip extraction stops at the end of the nDVR store.
     
  • appname - The live application in the Wowza Streaming Engine instance that has the nDVR store. 
     
  • instancename - The live application instance that has the nDVR store. The default value is _definst_ (the default live application instance).
     
  • command - Commands attached to the query to specify specific functions (in the form command=value). Supported command values are:
     
    • convert - extract on-demand clip from the specified nDVR store.
       
    • status - get conversion status for the specified nDVR store.
       
    • expire - clear the nDVR converter cache.
  • streamname - The base stream name in the nDVR store to convert, for example, myStream (not myStream.0). If you use the nDVR version archive strategy to store your nDVR recordings, you must specify the base stream name to convert all versions of the nDVR recording, which are stored in multiple content storage directories.

Example - Convert nDVR store

The following example conversion call converts the entire nDVR store:

http://[wowza-ip-address]:1935/converter?command=convert&streamname=myStream


The expected output would be similar to the following:
Application name : live
Application Instance name : _definst_
DVR Context Object : com.wowza.wms.dvr.DvrApplicationConverterContext@6b660be9
DVR Store Name : myStream contains version 0
DVR Store Name : myStream-destin contains version 0
DVR Store Name : myStream_160p contains version 0
DVR Store Name : myStream_360p contains version 0
DVR Store Name : wowzaStream contains version 0
DVR Store Conversion Selected 
DVR Store Name To Use : myStream.0
DVR Store Name To Use : myStream.0 start UTC : 1450099629964 
DVR Store Name To Use : myStream.0 end UTC : 1453934685390 
DVR Store Name To Use : myStream.0 start offset : 0 
DVR Store Name To Use : myStream.0 end offset : 0 
DVR Conversion started : myStream.0 Output filename : C:\Program Files (x86)\Wowza Media Systems\Wowza Streaming Engine 4.4.0\content\testfiles\myStream.0.mp4

Example - Convert nDVR store with offset 

The following example conversion call converts the entire nDVR store. It uses the startoffset parameter to skip the first 20 seconds of the nDVR recording:

http://[wowza-ip-address]:1935/converter?command=convert&streamname=myStream&startoffset=20000


The following example conversion call converts the entire nDVR store. It uses the endoffset parameter to skip the last 20 seconds of the nDVR recording:

http://[wowza-ip-address]:1935/converter?command=convert&streamname=myStream&endoffset=20000

Example - Clear nDVR store conversion cache 

Conversion request details for an nDVR store are cached for up to 30 minutes so that conversion status can be viewed and the request re-used for conversions of the same store in the future. Calls to convert the same nDVR store will use the cached request details, so if you need to adjust any conversion parameters for future conversions of the same store, you must clear the conversion cache. The following example conversion call clears the conversion cache for the nDVR converter:

http://[wowza-ip-address]:1935/converter?command=expire


This conversion request will show a list of nDVR stores before expiry and after. The following example shows one conversion being expired from the converter cache:
Application name : live
Application Instance name : _definst_
DVR Context Object : com.wowza.wms.dvr.DvrApplicationConverterContext@6b660be9
DVR Store Name : myStream contains version 0
DVR Store Name : myStream-destin contains version 0
DVR Store Name : myStream_160p contains version 0
DVR Store Name : myStream_360p contains version 0
DVR Store Name : wowzaStream contains version 0
DVR Converter - Force Expire Previous Conversions 
DVR Converter - Force Expire - Current Store : myStream.0 State : SUCCESSFUL
DVR Converter - Forcing Expiry 
Command and/or Stream Name not provided
 
Notes:
  • Clearing the conversion cache only removes nDVR store conversion request details from an internal conversion list. It does NOT remove the nDVR store from the system.
     
  • The nDVR stores that are actively being converted won't be expired by this command. Only nDVR stores that aren't being converted are expired.

Example - Get nDVR store conversion status

The following example conversion call displays the conversion status for the specified nDVR store:

http://[wowza-ip-address]:1935/converter?command=status&streamname=myStream


Example result (conversion not started):
Application name : live
Application Instance name : _definst_
DVR Context Object : com.wowza.wms.dvr.DvrApplicationConverterContext@6b660be9
DVR Store Name : myStream contains version 0
DVR Store Name : myStream-destin contains version 0
DVR Store Name : myStream_160p contains version 0
DVR Store Name : myStream_360p contains version 0
DVR Store Name : wowzaStream contains version 0
DVR Store Status Selected 
DVR Store Name To Use : myStream.0
State is : STOPPED

Example result (conversion in progress):
Application name : live
Application Instance name : _definst_
DVR Context Object : com.wowza.wms.dvr.DvrApplicationConverterContext@6b660be9
DVR Store Name : myStream contains version 0
DVR Store Name : myStream-destin contains version 0
DVR Store Name : myStream_160p contains version 0
DVR Store Name : myStream_360p contains version 0
DVR Store Name : wowzaStream contains version 0
DVR Store Status Selected
DVR Store Name To Use : myStream.0
State is : RUNNING

Example result (conversion completed):
Application name : live
Application Instance name : _definst_
DVR Context Object : com.wowza.wms.dvr.DvrApplicationConverterContext@6b660be9
DVR Store Name : myStream contains version 0
DVR Store Name : myStream-destin contains version 0
DVR Store Name : myStream_160p contains version 0
DVR Store Name : myStream_360p contains version 0
DVR Store Name : wowzaStream contains version 0
DVR Store Status Selected 
DVR Store Name To Use : myStream.0
State is : SUCCESSFUL

The possible states are STOPPED, INITIALIZING, RUNNING, SUCCESSFUL, and ERROR.

More resources