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.
- convert - extract on-demand clip from the specified nDVR store.
- 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
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
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
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
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.