多协议、性能稳定、丰富API的流媒体服务器软件
如何使用Wowza nDVR的录制API?
这篇文章用一个HTTPProvider演示了如何使用Wowza nDVR的录制API。

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