这篇文章通过一个HTTPProvider展示了如何使用Wowza nDVR API 来检查DVR的流录制,这个API使用dvrTime、packetTime、 UTC time 三类时间来查询。
时间单位
每一个nDVR 的切片的时间有三类时间单位:
主要的接口
每一个IDvrStreamStore 包含一个IDvrManifest, 它代表了DVR录制流的组成信息。
IDvrManifest 包含几个通道的信息,包括音频和视频通道。这个class 是DvrChannelManifest。这些通道由多个manifest记录组成,它包含了分别用三个时间单位表示的一个时间戳。
通过从IApplicationInstance 定位到音频和视频流,我们可以检测到第一条和最后一条记录信息,并确定3个时间单位的开始和结束的时间。
HTTP Provider的例子
下面的例子用 HTTPProvider 机制展示了如何查询一个流的所有相关DVR录制信息。 这个例子展示了DVR录制是否正在进行、是否包含音频或视频,并报告DVR的开始和结束时间、数据包和UTC时间标。
这里提供的代码只是一个示例,你可以很容易修改它以适应你的需求。
HTTPProvider是wowza media server的扩展,它可以监听来自[install-dir]/conf/VHost.xml中的定义的HostPort上的HTTP请求,你可以通过URL请求来控制它。 在用户使用指南的对应章节可以获得更多细节信息。
使用Wowza IDE将这个class编译为一个jar文件,并将其拷贝到[install-dir]/lib文件夹下。 然后将这个HTTPProvider添加到/conf/VHost.xml /HostPort (Port 8086) /HTTPProviders里面。了解更多Wowza产品细节 这个HTTPProvider应该被添加到ServiceVersion HTTPProvider的上面。 在用户使用指南上详细解释了如何开发和部署HTTPProvider。
使用HTTP Provider
nDVR的录制,可以通过URL来访问HTTPProvider的方式来进行控制。格式如下:
其中:
这个HTTP Provider将想浏览器返回一个HTML响应,如下:
代理示例
下面是一个查询DVR存储信息的代码示例:
注意:这个功能需要Wowza Media Server® 3.0.3.12及以上版本的支持。
时间单位
每一个nDVR 的切片的时间有三类时间单位:
- DVR time:这是一个以毫秒为单位的DVR录制时间。第一个切片的时间总是从0开始,以后切片的时间从这里开始。了解Wowza产品细节 如果输入流有空隙(间隔),它可能是因为编码器有一段时间停止了发送数据或者是因为nDVR工作在append模式,但DVR时间不会包含空隙。
- Packet time: 编码器对输出的音频和视频数据进行打包时的时间戳。nDVR切片将保留这个packet time.
- UTC time. 当输入流进入nDVR 模块时,数据包会被打上当前的UTC时间。注意,这不是数据包离开编码器的时间,这是Wowza nDVR收到数据包时的时间。
主要的接口
每一个IDvrStreamStore 包含一个IDvrManifest, 它代表了DVR录制流的组成信息。
IDvrManifest 包含几个通道的信息,包括音频和视频通道。这个class 是DvrChannelManifest。这些通道由多个manifest记录组成,它包含了分别用三个时间单位表示的一个时间戳。
通过从IApplicationInstance 定位到音频和视频流,我们可以检测到第一条和最后一条记录信息,并确定3个时间单位的开始和结束的时间。
HTTP Provider的例子
下面的例子用 HTTPProvider 机制展示了如何查询一个流的所有相关DVR录制信息。 这个例子展示了DVR录制是否正在进行、是否包含音频或视频,并报告DVR的开始和结束时间、数据包和UTC时间标。
这里提供的代码只是一个示例,你可以很容易修改它以适应你的需求。
HTTPProvider是wowza media server的扩展,它可以监听来自[install-dir]/conf/VHost.xml中的定义的HostPort上的HTTP请求,你可以通过URL请求来控制它。 在用户使用指南的对应章节可以获得更多细节信息。
使用Wowza IDE将这个class编译为一个jar文件,并将其拷贝到[install-dir]/lib文件夹下。 然后将这个HTTPProvider添加到/conf/VHost.xml /HostPort (Port 8086) /HTTPProviders里面。了解更多Wowza产品细节 这个HTTPProvider应该被添加到ServiceVersion HTTPProvider的上面。 在用户使用指南上详细解释了如何开发和部署HTTPProvider。
Code:
<HTTPProvider> <BaseClass>com.wowza.wms.plugin.test.dvr.api.HTTPDvrStreamQuery</BaseClass> <RequestFilters>dvrstreamquery*</RequestFilters> <AuthenticationMethod>none</AuthenticationMethod> </HTTPProvider> <!-- default provider must go at the end --> <HTTPProvider> <BaseClass>com.wowza.wms.http.HTTPServerVersion</BaseClass> <RequestFilters>*</RequestFilters> <AuthenticationMethod>none</AuthenticationMethod> </HTTPProvider>
使用HTTP Provider
nDVR的录制,可以通过URL来访问HTTPProvider的方式来进行控制。格式如下:
Code:
http://[wowza-ip-address]:8086/dvrstreamquery?action=query&app=[application-name]&streamname=[stream-name][&forceload=true]
- [wowza-ip-address]: 运行Wowza Media Server的服务器IP地址
- app: 承载输入流的应用的应用名称
- streamname: 将要被录制的输入流的名称
- forceload: (可选) 这个provider默认只检索已经被Wowza Media Server加载的DVR录制。添加这个参数可以强制加载这个流的DVR录制信息。
这个HTTP Provider将想浏览器返回一个HTML响应,如下:
Code:
query myStream: Store:myStream.0 isLive: false hasAudio: true hasVideo: true dvrStart: 0.000 dvrEnd: 48.466 duration: 48.466 packetStart: 0 packetEnd: 48466 utcStart: 2011-Dec-20 14:37:20 utcEnd: 2011-Dec-20 14:38:08 Store:myStream.1 isLive: true hasAudio: true hasVideo: true dvrStart: 0.000 dvrEnd: 1.648 duration: 1.648 packetStart: 1449298 packetEnd: 1450946 utcStart: 2012-Jan-03 17:06:06 utcEnd: 2012-Jan-03 17:06:07
代理示例
下面是一个查询DVR存储信息的代码示例:
Code:
package com.wowza.wms.plugin.test.dvr.api; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.*; import com.wowza.wms.application.IApplicationInstance; import com.wowza.wms.dvr.*; import com.wowza.wms.http.*; import com.wowza.wms.logging.WMSLoggerFactory; import com.wowza.wms.plugin.test.dvr.api.HTTPDvrStreamQuery.StartEndTimes; import com.wowza.wms.stream.mediacaster.MediaStreamMediaCasterUtils; import com.wowza.wms.vhost.*; // Usage: http://[wowza-ip-address]:8086/dvrstreamquery?action=query&app=[application-name]&streamname=[stream-name][&forceload=true] public class HTTPDvrStreamQuery extends HTTProvider2Base { private static final String CLASSNAME = "HTTPDvrStreamQuery"; private static final Class<HTTPDvrStreamQuery> CLASS = HTTPDvrStreamQuery.class; 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 = ""; boolean forceLoad = false; 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); } else { report += "<BR>" + "streamname" + " is required"; } WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME + " streamName: " + streamName); if (params.containsKey("forceload")) { String forceLoadString = params.get("forceload").get(0); forceLoad = Boolean.parseBoolean(forceLoadString); WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME + " forceload: " + forceLoadString); } } catch (Exception ex) { report = "Error: " + ex.getMessage(); } } else { report = "Nothing to do."; } try { IApplicationInstance appInstance = vhost.getApplication(app).getAppInstance("_definst_"); // If no error if (report.equalsIgnoreCase("")) { if (action.equalsIgnoreCase("query")) { WMSLoggerFactory.getLogger(CLASS).info(String.format("%s.%s: %s", CLASSNAME, "query", 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); String result = queryDvrStreamInfo(appInstance, streamName, forceLoad); report = action + " " + streamName + ":"; report = report + "<br><p>\n" + result; } else { report = "Action: "+action + " is not valid."; } } } catch (Exception e) { report = "Error: " + e.getMessage(); } String retStr = "<html><head><title>HTTPProvider DvrStreamQuery</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 String queryDvrStreamInfo(IApplicationInstance appInstance, String streamName, boolean forceLoad) { StringBuffer sb = new StringBuffer(); IDvrStreamManager dvrMgr = DvrStreamManagerUtils.getStreamManager(appInstance, IDvrConstants.DVR_STREAMING_PACKETIZER_ID, streamName, forceLoad); if (dvrMgr == null) { sb.append("Stream "+streamName+" not loaded."); } else { List<IDvrStreamStore> stores = dvrMgr.getStreamStores(); for (IDvrStreamStore store : stores) { sb.append(queryDvrStoreInfo(store)); } } return sb.toString(); } public String queryDvrStoreInfo(IDvrStreamStore store) { StringBuffer sb = new StringBuffer(); if (store != null) { boolean hasAudio = store.hasAudio(); boolean hasVideo = store.hasVideo(); boolean isLive = store.isLive(); sb.append(String.format("<BR>Store:%s", store.getStreamName())); String result = String.format("<BR> isLive: %s hasAudio: %s hasVideo: %s", isLive, hasAudio, hasVideo); sb.append(result); long duration = -1; StartEndTimes dvrTimes = queryDvrStartStop(store); if (dvrTimes != null) { duration = dvrTimes.end - dvrTimes.start; String s = String.format("<BR> dvrStart: %s dvrEnd: %s duration: %s", formatDvrTime(dvrTimes.start), formatDvrTime(dvrTimes.end), formatDvrTime(duration)); sb.append(s); } StartEndTimes packetTimes = queryPacketStartStop(store); if (packetTimes != null) { String s = String.format("<BR> packetStart: %s packetEnd: %s", formatPacketTime(packetTimes.start), formatPacketTime(packetTimes.end)); sb.append(s); } StartEndTimes utcTimes = queryUtcStartStop(store); if (utcTimes != null) { String s = String.format("<BR> utcStart: %s utcEnd: %s", formatUtcTime(utcTimes.start), formatUtcTime(utcTimes.end)); sb.append(s); } sb.append("<P>"); } return sb.toString(); } protected StartEndTimes queryDvrStartStop(IDvrStreamStore store) { if (store == null) { return null; } DvrChannelManifest manifest = getVideoOrAudioManifest(store); if (manifest == null) { return null; } DvrManifestEntry firstEntry = manifest.getFirstEntry(); DvrManifestEntry lastEntry = manifest.getLastRecordedEntry(); long start = firstEntry.getStartTimecode(); long end = lastEntry.getStopTimecode(); return new StartEndTimes(start, end); } protected StartEndTimes queryPacketStartStop(IDvrStreamStore store) { if (store == null) { return null; } DvrChannelManifest manifest = getVideoOrAudioManifest(store); if (manifest == null) { return null; } DvrManifestEntry firstEntry = manifest.getFirstEntry(); DvrManifestEntry lastEntry = manifest.getLastRecordedEntry(); long start = firstEntry.getPacketStartTime(); long end = lastEntry.getPacketStartTime() + lastEntry.getDuration(); return new StartEndTimes(start, end); } protected StartEndTimes queryUtcStartStop(IDvrStreamStore store) { if (store == null) { return null; } DvrChannelManifest manifest = getVideoOrAudioManifest(store); if (manifest == null) { return null; } DvrManifestEntry firstEntry = manifest.getFirstEntry(); DvrManifestEntry lastEntry = manifest.getLastRecordedEntry(); long start = firstEntry.getUtcStartTime(); long end = lastEntry.getUtcStartTime() + lastEntry.getDuration(); return new StartEndTimes(start, end); } private DvrChannelManifest getVideoOrAudioManifest(IDvrStreamStore store) { IDvrManifest manifest = store.getManifest(); boolean hasAudio = store.hasAudio(); boolean hasVideo = store.hasVideo(); DvrChannelManifest channelManifest = null; if (hasVideo) { channelManifest = manifest.getManifestChannel(IVHost.CONTENTTYPE_VIDEO); } else if (hasAudio) { channelManifest = manifest.getManifestChannel(IVHost.CONTENTTYPE_AUDIO); } return channelManifest; } protected String formatUtcTime(long utc) { final String UTC_FORMAT = "yyyy-MMM-dd HH:mm:ss"; SimpleDateFormat dateFormatLocal = new SimpleDateFormat(UTC_FORMAT); return dateFormatLocal.format(new Date(utc)); } protected String formatDvrTime(long t) { return String.format("%.3f", t/1000.0); } protected String formatPacketTime(long t) { return String.format("%s", t); } public class StartEndTimes { long start = -1; long end = -1; public StartEndTimes(long start, long end) { this.start = start; this.end = end; } } }了解更多Wowza产品细节