• How to extract MP4-formatted files from DVR streams using 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.

    Contents


    Compiling the Converter HTTP Provider
    Live to VOD clip extraction examples
    More resources

    Compiling the Converter HTTP Provider


    Compile the following Converter HTTP Provider example with the Wowza IDE.
    package com.wowza.demo.ndvrconverter;
    
    import java.io.*;
    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.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 HTTProvider2Base
    {
    
    	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. The default value is live (uses the default application named live).

    • 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




    Originally Published: For Wowza Streaming Engine 4.4.0 on 02-02-2016.

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