Extend onvif server to support Unifi Protect
This commit is contained in:
@@ -70,6 +70,10 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) {
|
||||
// important for Hass: Media section
|
||||
res = onvif.GetCapabilitiesResponse(r.Host)
|
||||
|
||||
case onvif.ActionGetServices:
|
||||
// important for Unifi: Media section
|
||||
res = onvif.GetServicesResponse(r.Host)
|
||||
|
||||
case onvif.ActionGetSystemDateAndTime:
|
||||
// important for Hass
|
||||
res = onvif.GetSystemDateAndTimeResponse()
|
||||
@@ -95,8 +99,13 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
case onvif.ActionGetProfiles:
|
||||
// important for Hass: H264 codec, width, height
|
||||
// important for Unifi: framerate, bitrate, quality
|
||||
res = onvif.GetProfilesResponse(streams.GetAll())
|
||||
|
||||
case onvif.ActionGetVideoSources:
|
||||
// important for Unifi: framerate, resolution
|
||||
res = onvif.GetVideoSourcesResponse(streams.GetAll())
|
||||
|
||||
case onvif.ActionGetStreamUri:
|
||||
host, _, err := net.SplitHostPort(r.Host)
|
||||
if err != nil {
|
||||
@@ -107,6 +116,10 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) {
|
||||
uri := "rtsp://" + host + ":" + rtsp.Port + "/" + onvif.FindTagValue(b, "ProfileToken")
|
||||
res = onvif.GetStreamUriResponse(uri)
|
||||
|
||||
case onvif.ActionGetSnapshotUri:
|
||||
uri := "http://" + r.Host + "/api/frame.jpeg?src=" + onvif.FindTagValue(b, "ProfileToken")
|
||||
res = onvif.GetSnapshotUriResponse(uri)
|
||||
|
||||
default:
|
||||
http.Error(w, "unsupported action", http.StatusBadRequest)
|
||||
log.Debug().Msgf("[onvif] unsupported request:\n%s", b)
|
||||
|
||||
+90
-10
@@ -16,6 +16,7 @@ const (
|
||||
ActionGetServiceCapabilities = "GetServiceCapabilities"
|
||||
ActionGetProfiles = "GetProfiles"
|
||||
ActionGetStreamUri = "GetStreamUri"
|
||||
ActionGetSnapshotUri = "GetSnapshotUri"
|
||||
ActionSystemReboot = "SystemReboot"
|
||||
|
||||
ActionGetServices = "GetServices"
|
||||
@@ -65,6 +66,32 @@ func GetCapabilitiesResponse(host string) string {
|
||||
</s:Envelope>`
|
||||
}
|
||||
|
||||
func GetServicesResponse(host string) string {
|
||||
return `<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
|
||||
<s:Body>
|
||||
<tds:GetServicesResponse xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
|
||||
<tds:Service>
|
||||
<tds:Namespace>http://www.onvif.org/ver10/device/wsdl</tds:Namespace>
|
||||
<tds:XAddr>http://` + host + `/onvif/device_service</tds:XAddr>
|
||||
<tds:Version>
|
||||
<tds:Major>2</tds:Major>
|
||||
<tds:Minor>5</tds:Minor>
|
||||
</tds:Version>
|
||||
</tds:Service>
|
||||
<tds:Service>
|
||||
<tds:Namespace>http://www.onvif.org/ver10/media/wsdl</tds:Namespace>
|
||||
<tds:XAddr>http://` + host + `/onvif/media_service</tds:XAddr>
|
||||
<tds:Version>
|
||||
<tds:Major>2</tds:Major>
|
||||
<tds:Minor>5</tds:Minor>
|
||||
</tds:Version>
|
||||
</tds:Service>
|
||||
</tds:GetServicesResponse>
|
||||
</s:Body>
|
||||
</s:Envelope>`
|
||||
}
|
||||
|
||||
func GetSystemDateAndTimeResponse() string {
|
||||
loc := time.Now()
|
||||
utc := loc.UTC()
|
||||
@@ -142,7 +169,7 @@ func GetServiceCapabilitiesResponse() string {
|
||||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
|
||||
<s:Body>
|
||||
<trt:GetServiceCapabilitiesResponse xmlns:trt="http://www.onvif.org/ver10/media/wsdl">
|
||||
<trt:Capabilities SnapshotUri="false" Rotation="false" VideoSourceMode="false" OSD="false" TemporaryOSDText="false" EXICompression="false">
|
||||
<trt:Capabilities SnapshotUri="true" Rotation="false" VideoSourceMode="false" OSD="false" TemporaryOSDText="false" EXICompression="false">
|
||||
<trt:StreamingCapabilities RTPMulticast="false" RTP_TCP="false" RTP_RTSP_TCP="true" NonAggregateControl="false" NoRTSPStreaming="false" />
|
||||
</trt:Capabilities>
|
||||
</trt:GetServiceCapabilitiesResponse>
|
||||
@@ -171,14 +198,27 @@ func GetProfilesResponse(names []string) string {
|
||||
for i, name := range names {
|
||||
buf.WriteString(`
|
||||
<trt:Profiles token="` + name + `" fixed="true">
|
||||
<tt:Name>` + name + `</tt:Name>
|
||||
<tt:VideoEncoderConfiguration token="` + strconv.Itoa(i) + `">
|
||||
<tt:Encoding>H264</tt:Encoding>
|
||||
<tt:Resolution>
|
||||
<tt:Width>1920</tt:Width>
|
||||
<tt:Height>1080</tt:Height>
|
||||
</tt:Resolution>
|
||||
</tt:VideoEncoderConfiguration>
|
||||
<trt:Name>` + name + `</trt:Name>
|
||||
<trt:VideoEncoderConfiguration token="` + strconv.Itoa(i) + `">
|
||||
<trt:Name>` + name + `</trt:Name>
|
||||
<trt:Encoding>H264</trt:Encoding>
|
||||
<trt:Resolution>
|
||||
<trt:Width>1920</trt:Width>
|
||||
<trt:Height>1080</trt:Height>
|
||||
</trt:Resolution>
|
||||
<trt:RateControl>
|
||||
<trt:FrameRateLimit>29.97003</trt:FrameRateLimit>
|
||||
<trt:EncodingInterval>1</trt:EncodingInterval>
|
||||
<trt:BitrateLimit>5000</trt:BitrateLimit>
|
||||
</trt:RateControl>
|
||||
<trt:Quality>4</trt:Quality>
|
||||
<trt:SessionTimeout>PT1000S</trt:SessionTimeout>
|
||||
</trt:VideoEncoderConfiguration>
|
||||
<trt:VideoSourceConfiguration token="` + strconv.Itoa(i) + `">
|
||||
<trt:Name>` + name + `</trt:Name>
|
||||
<trt:SourceToken>` + strconv.Itoa(i) + `</trt:SourceToken>
|
||||
<trt:Bounds x="0" y="0" width="1920" height="1080"></trt:Bounds>
|
||||
</trt:VideoSourceConfiguration>
|
||||
</trt:Profiles>`)
|
||||
}
|
||||
|
||||
@@ -190,15 +230,55 @@ func GetProfilesResponse(names []string) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
|
||||
func GetVideoSourcesResponse(names []string) string {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.WriteString(`<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
|
||||
<s:Body>
|
||||
<trt:GetVideoSourcesResponse xmlns:trt="http://www.onvif.org/ver10/media/wsdl">`)
|
||||
|
||||
for i, _ := range names {
|
||||
buf.WriteString(`
|
||||
<trt:VideoSources token="` + strconv.Itoa(i) + `">
|
||||
<trt:Framerate>29.97003</trt:Framerate>
|
||||
<trt:Resolution>
|
||||
<trt:Width>1920</trt:Width>
|
||||
<trt:Height>1080</trt:Height>
|
||||
</trt:Resolution>
|
||||
</trt:VideoSources>`)
|
||||
}
|
||||
|
||||
buf.WriteString(`
|
||||
</trt:GetVideoSourcesResponse >
|
||||
</s:Body>
|
||||
</s:Envelope>`)
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func GetStreamUriResponse(uri string) string {
|
||||
return `<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
|
||||
<s:Body>
|
||||
<trt:GetStreamUriResponse xmlns:trt="http://www.onvif.org/ver10/media/wsdl">
|
||||
<trt:MediaUri>
|
||||
<tt:Uri xmlns:tt="http://www.onvif.org/ver10/schema">` + uri + `</tt:Uri>
|
||||
<trt:Uri>` + uri + `</trt:Uri>
|
||||
</trt:MediaUri>
|
||||
</trt:GetStreamUriResponse>
|
||||
</s:Body>
|
||||
</s:Envelope>`
|
||||
}
|
||||
|
||||
func GetSnapshotUriResponse(uri string) string {
|
||||
return `<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
|
||||
<s:Body>
|
||||
<trt:GetSnapshotUriResponse xmlns:trt="http://www.onvif.org/ver10/media/wsdl">
|
||||
<trt:MediaUri>
|
||||
<trt:Uri>` + uri + `</trt:Uri>
|
||||
</trt:MediaUri>
|
||||
</trt:GetSnapshotUriResponse>
|
||||
</s:Body>
|
||||
</s:Envelope>`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user