Merge pull request #1520 from acortelyou/feat/unifi

Extend onvif server to support Unifi Protect
This commit is contained in:
Alex X
2024-12-30 20:33:33 +03:00
committed by GitHub
2 changed files with 115 additions and 31 deletions
+10
View File
@@ -70,6 +70,9 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) {
// important for Hass: Media section
res = onvif.GetCapabilitiesResponse(r.Host)
case onvif.ActionGetServices:
res = onvif.GetServicesResponse(r.Host)
case onvif.ActionGetSystemDateAndTime:
// important for Hass
res = onvif.GetSystemDateAndTimeResponse()
@@ -97,6 +100,9 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) {
// important for Hass: H264 codec, width, height
res = onvif.GetProfilesResponse(streams.GetAll())
case onvif.ActionGetVideoSources:
res = onvif.GetVideoSourcesResponse(streams.GetAll())
case onvif.ActionGetStreamUri:
host, _, err := net.SplitHostPort(r.Host)
if err != nil {
@@ -107,6 +113,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)
+105 -31
View File
@@ -16,6 +16,7 @@ const (
ActionGetServiceCapabilities = "GetServiceCapabilities"
ActionGetProfiles = "GetProfiles"
ActionGetStreamUri = "GetStreamUri"
ActionGetSnapshotUri = "GetSnapshotUri"
ActionSystemReboot = "SystemReboot"
ActionGetServices = "GetServices"
@@ -45,23 +46,49 @@ func GetRequestAction(b []byte) string {
func GetCapabilitiesResponse(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:GetCapabilitiesResponse xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
<tds:Capabilities xmlns:tt="http://www.onvif.org/ver10/schema">
<tt:Device>
<tt:XAddr>http://` + host + `/onvif/device_service</tt:XAddr>
</tt:Device>
<tt:Media>
<tt:XAddr>http://` + host + `/onvif/media_service</tt:XAddr>
<tt:StreamingCapabilities>
<tt:RTPMulticast>false</tt:RTPMulticast>
<tt:RTP_TCP>false</tt:RTP_TCP>
<tt:RTP_RTSP_TCP>true</tt:RTP_RTSP_TCP>
</tt:StreamingCapabilities>
</tt:Media>
</tds:Capabilities>
</tds:GetCapabilitiesResponse>
</s:Body>
<s:Body>
<tds:GetCapabilitiesResponse xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
<tds:Capabilities xmlns:tt="http://www.onvif.org/ver10/schema">
<tt:Device>
<tt:XAddr>http://` + host + `/onvif/device_service</tt:XAddr>
</tt:Device>
<tt:Media>
<tt:XAddr>http://` + host + `/onvif/media_service</tt:XAddr>
<tt:StreamingCapabilities>
<tt:RTPMulticast>false</tt:RTPMulticast>
<tt:RTP_TCP>false</tt:RTP_TCP>
<tt:RTP_RTSP_TCP>true</tt:RTP_RTSP_TCP>
</tt:StreamingCapabilities>
</tt:Media>
</tds:Capabilities>
</tds:GetCapabilitiesResponse>
</s:Body>
</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>`
}
@@ -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>
@@ -170,21 +197,55 @@ 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:Profiles>`)
<trt:Profiles token="` + name + `" fixed="true">
<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:RateControl>
</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>`)
}
buf.WriteString(`
</trt:GetProfilesResponse>
</s:Body>
</trt:GetProfilesResponse>
</s:Body>
</s:Envelope>`)
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: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()
@@ -196,9 +257,22 @@ func GetStreamUriResponse(uri string) string {
<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>`
}