配置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;
}
}
