配置nDVR
默认情况下,nDVR在接收到输入流后即开始启动录制。 然而,你的Application.xml文件DVR/Properties中的属性startRecordingOnStartup可以被设置为false。 这将导致录制功能被初始化,但并不会立即开始录制,直到ILiveStreamDvrRecorder.startRecording()被调用。
<Property> <Name>startRecordingOnStartup</Name> <Value>false</Value> <Type>boolean</Type> </Property>nDVR录制API
整个录制流程包括:
- 1、通过IMediaStream.getDvrRecorder()从直播流上下文环境获得实现处理录制工作的class
- 2、(可选)通过ILiveStreamDvrRecorder.setRecordingName()为录制设置一个名称
- 3、通过ILiveStreamDvrRecorder.startRecording()开始录制
- 4、录制开始以后,通过ILiveStreamDvrRecorder.stopRecording()停止录制
使用上述API的HTTP Provider例子
这个例子通过HTTPProvider的机制演示了如何使用上述nDVR录制API。
HTTPProvider是对Wowza Media Server的一个扩展,它可以运行在[install-dir]/conf/VHost.xml文件中定义的HostPort上,并通过URL访问来控制。 在用户使用指南的对应章节可以获得更多的细节信息。下面的class是一个HTTPProvider,它可以控制录制的开始和停止。
使用Wowza IDE将这个class编译到一个jar文件中,然后将其拷贝到[install-dir]/lib文件夹下。 然后将这个HTTPProvider添加到/conf/VHost.xml /HostPort (Port 8086) /HTTPProviders里面。 这个HTTPProvider应该被添加到ServiceVesion HTTPProvider的上面。
<HTTPProvider> <BaseClass>com.wowza.wms.plugin.dvrstreamrecord.HTTPDvrStreamRecord </BaseClass> <RequestFilters>dvrstreamrecord*</RequestFilters> <AuthenticationMethod>none</AuthenticationMethod> </HTTPProvider>
使用HTTP Provider
你可以通过URL访问来用HTTPProvider控制nDVR的录制功能。URL的格式是:
http://[wowza-ip-address]:8086/dvrstreamrecord?app=[application-name]&streamname=[stream-name]&recordingname=[recording-name]&action=[start|stop]其中:
- [wowza-ip-address]: 运行Wowza Media Server的服务器IP地址
- app: 承载这个流的应用名字
- streamname: 要录制的输入流的名字
- recordingname: (可选) 录制文件的存储名字。如果没有设置,默认采用streamname。
设置RecordingName
如果设置了recordingName,录制的文件将不会被存储在[install-dir]/dvr/[app-name]/[app-inst]/[streamName].[version]里面,而是会被存储在[install-dir]/dvr/[app-name]/[app-inst]/[recordingName].[version]里面。
然后,当播放录制的流时,你应该在播放URL中用[recordingName]而不是[streamName]。 例如,如果输入流是myStream,录制流是myRecording,那么你HLS协议的URL为:
http://[wowza-address]:1935/dvr/myRecording/playlist.m3u8?DVR
代码例子
下面是控制录制的代码例子:
package com.wowza.wms.plugin.dvrstreamrecord; import java.io.OutputStream; import java.util.*; import com.wowza.wms.application.IApplicationInstance; import com.wowza.wms.dvr.*; import com.wowza.wms.dvr.io.IDvrFileSystem; import com.wowza.wms.http.*; import com.wowza.wms.logging.WMSLoggerFactory; import com.wowza.wms.stream.IMediaStream; import com.wowza.wms.stream.livedvr.*; import com.wowza.wms.stream.mediacaster.MediaStreamMediaCasterUtils; import com.wowza.wms.vhost.*; public class HTTPDvrStreamRecord extends HTTProvider2Base { private static final String CLASSNAME = "HTTPDvrStreamRecord"; private static final Class<HTTPDvrStreamRecord> CLASS = HTTPDvrStreamRecord.class; private Map<String, ILiveStreamDvrRecorder> dvrRecorders = new HashMap<String, ILiveStreamDvrRecorder>(); public void onHTTPRequest(IVHost vhost, IHTTPRequest req, IHTTPResponse resp) { if (!doHTTPAuthentication(vhost, req, resp)) { return; } WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME + " HTTPRequest"); Map<String, List<String>> params = req.getParameterMap(); String action = ""; String app = ""; String streamName = ""; String report = ""; String recordingName = ""; if (req.getMethod().equalsIgnoreCase("get") || req.getMethod().equalsIgnoreCase("post")) { req.parseBodyForParams(true); try { if (params.containsKey("action")) { action = params.get("action").get(0); } else { report += "<BR>" + "action" + " is required"; } WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME + " action: " + action); if (params.containsKey("app")) { app = params.get("app").get(0); } else { report += "<BR>" + "app" + " is required"; } WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME + " app: " + app); if (params.containsKey("streamname")) { streamName = params.get("streamname").get(0); recordingName = streamName; // default to stream name } else { report += "<BR>" + "streamname" + " is required"; } WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME + " streamName: " + streamName); // If recordingName is specified, use it instead if (params.containsKey("recordingname")) { recordingName = params.get("recordingname").get(0); WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME + " recordingName: " + recordingName); } } catch (Exception ex) { report = "Error: " + ex.getMessage(); } } else { report = "Nothing to do."; } try { IApplicationInstance appInstance = vhost.getApplication(app).getAppInstance("_definst_"); if (!appInstance.getPublishStreamNames().contains(streamName)) { report = "Live stream " + streamName + " does not exist."; } if (action.equalsIgnoreCase("start") && report.equalsIgnoreCase("")) { WMSLoggerFactory.getLogger(CLASS).info(String.format("%s.%s: %s", CLASSNAME, "start", streamName)); String streamTypeStr = appInstance.getStreamType(); boolean isLiveRepeaterEdge = false; while (true) { StreamList streamDefs = appInstance.getVHost().getStreamTypes(); StreamItem streamDef = streamDefs.getStreamDef(streamTypeStr); if (streamDef == null) break; isLiveRepeaterEdge = streamDef.getProperties().getPropertyBoolean("isLiveRepeaterEdge", isLiveRepeaterEdge); break; } if (isLiveRepeaterEdge) streamName = MediaStreamMediaCasterUtils.mapMediaCasterName(appInstance, null, streamName); IMediaStream stream = appInstance.getStreams().getStream(streamName); if (stream != null) { startRecording(stream, recordingName); report = action + " " + streamName + " as " + recordingName; } else { WMSLoggerFactory.getLogger(CLASS).warn(String.format("%s.%s: stream '%s' not found.", CLASSNAME, "start", streamName)); report = "Stream Not Found: " + streamName; } } else if (action.equalsIgnoreCase("stop") & report.equalsIgnoreCase("")) { WMSLoggerFactory.getLogger(CLASS).info(String.format("%s.%s: %s", CLASSNAME, "stop", streamName)); String path = stopRecording(streamName); report = action + " " + streamName + " " + path; } } catch (Exception e) { report = "Error: " + e.getMessage(); } String retStr = "<html><head><title>HTTPProvider DvrStreamRecord</title></head><body><h1>" + report + "</h1></body></html>"; try { OutputStream out = resp.getOutputStream(); byte[] outBytes = retStr.getBytes(); out.write(outBytes); } catch (Exception e) { WMSLoggerFactory.getLogger(CLASS).error(CLASSNAME + ": " + e.toString()); } } public void startRecording(IMediaStream stream, String recordingName) { String streamName = stream.getName(); // add it to the recorders list synchronized (dvrRecorders) { // Stop previous recorder ILiveStreamDvrRecorder prevRecorder = dvrRecorders.get(streamName); if (prevRecorder != null && prevRecorder.isRecording()) { prevRecorder.stopRecording(); } // get the stream's DVR recorder and save it in a map of recorders ILiveStreamDvrRecorder dvrRecorder = stream.getDvrRecorder(IDvrConstants.DVR_DEFAULT_RECORDER_ID); if (dvrRecorder != null) { if (dvrRecorder.isRecording()) { dvrRecorder.stopRecording(); } // start recording dvrRecorder.setRecordingName(recordingName); dvrRecorder.startRecording(); dvrRecorders.put(streamName, dvrRecorder); } else { WMSLoggerFactory.getLogger(CLASS).warn(String.format("%s.%s: DVR Recorder not found for stream '%s'.", CLASSNAME, "start", streamName)); } } } public String stopRecording(String streamName) { String path = ""; ILiveStreamDvrRecorder dvrRecorder = null; synchronized (dvrRecorders) { dvrRecorder = dvrRecorders.remove(streamName); } if (dvrRecorder != null) { IDvrStreamManager dvrManager = dvrRecorder.getDvrManager(); if (dvrManager != null) { IDvrStreamStore store = dvrManager.getRecordingStreamStore(); IDvrFileSystem fs = store.getFileSystem(); path = fs.getBasePath(); } // stop recording dvrRecorder.stopRecording(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } else { WMSLoggerFactory.getLogger(CLASS).warn(String.format("%s.%s: DVR Manager not found for stream '%s'.", CLASSNAME, "stop", streamName)); } return path; } }