Skip to content

Commit 2c3219f

Browse files
authored
Merge pull request #1520 from acortelyou/feat/unifi
Extend onvif server to support Unifi Protect
2 parents a3f084d + cf88bf9 commit 2c3219f

File tree

2 files changed

+115
-31
lines changed

2 files changed

+115
-31
lines changed

internal/onvif/init.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) {
7070
// important for Hass: Media section
7171
res = onvif.GetCapabilitiesResponse(r.Host)
7272

73+
case onvif.ActionGetServices:
74+
res = onvif.GetServicesResponse(r.Host)
75+
7376
case onvif.ActionGetSystemDateAndTime:
7477
// important for Hass
7578
res = onvif.GetSystemDateAndTimeResponse()
@@ -97,6 +100,9 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) {
97100
// important for Hass: H264 codec, width, height
98101
res = onvif.GetProfilesResponse(streams.GetAll())
99102

103+
case onvif.ActionGetVideoSources:
104+
res = onvif.GetVideoSourcesResponse(streams.GetAll())
105+
100106
case onvif.ActionGetStreamUri:
101107
host, _, err := net.SplitHostPort(r.Host)
102108
if err != nil {
@@ -107,6 +113,10 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) {
107113
uri := "rtsp://" + host + ":" + rtsp.Port + "/" + onvif.FindTagValue(b, "ProfileToken")
108114
res = onvif.GetStreamUriResponse(uri)
109115

116+
case onvif.ActionGetSnapshotUri:
117+
uri := "http://" + r.Host + "/api/frame.jpeg?src=" + onvif.FindTagValue(b, "ProfileToken")
118+
res = onvif.GetSnapshotUriResponse(uri)
119+
110120
default:
111121
http.Error(w, "unsupported action", http.StatusBadRequest)
112122
log.Debug().Msgf("[onvif] unsupported request:\n%s", b)

pkg/onvif/server.go

Lines changed: 105 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const (
1616
ActionGetServiceCapabilities = "GetServiceCapabilities"
1717
ActionGetProfiles = "GetProfiles"
1818
ActionGetStreamUri = "GetStreamUri"
19+
ActionGetSnapshotUri = "GetSnapshotUri"
1920
ActionSystemReboot = "SystemReboot"
2021

2122
ActionGetServices = "GetServices"
@@ -45,23 +46,49 @@ func GetRequestAction(b []byte) string {
4546
func GetCapabilitiesResponse(host string) string {
4647
return `<?xml version="1.0" encoding="utf-8" standalone="yes"?>
4748
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
48-
<s:Body>
49-
<tds:GetCapabilitiesResponse xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
50-
<tds:Capabilities xmlns:tt="http://www.onvif.org/ver10/schema">
51-
<tt:Device>
52-
<tt:XAddr>http://` + host + `/onvif/device_service</tt:XAddr>
53-
</tt:Device>
54-
<tt:Media>
55-
<tt:XAddr>http://` + host + `/onvif/media_service</tt:XAddr>
56-
<tt:StreamingCapabilities>
57-
<tt:RTPMulticast>false</tt:RTPMulticast>
58-
<tt:RTP_TCP>false</tt:RTP_TCP>
59-
<tt:RTP_RTSP_TCP>true</tt:RTP_RTSP_TCP>
60-
</tt:StreamingCapabilities>
61-
</tt:Media>
62-
</tds:Capabilities>
63-
</tds:GetCapabilitiesResponse>
64-
</s:Body>
49+
<s:Body>
50+
<tds:GetCapabilitiesResponse xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
51+
<tds:Capabilities xmlns:tt="http://www.onvif.org/ver10/schema">
52+
<tt:Device>
53+
<tt:XAddr>http://` + host + `/onvif/device_service</tt:XAddr>
54+
</tt:Device>
55+
<tt:Media>
56+
<tt:XAddr>http://` + host + `/onvif/media_service</tt:XAddr>
57+
<tt:StreamingCapabilities>
58+
<tt:RTPMulticast>false</tt:RTPMulticast>
59+
<tt:RTP_TCP>false</tt:RTP_TCP>
60+
<tt:RTP_RTSP_TCP>true</tt:RTP_RTSP_TCP>
61+
</tt:StreamingCapabilities>
62+
</tt:Media>
63+
</tds:Capabilities>
64+
</tds:GetCapabilitiesResponse>
65+
</s:Body>
66+
</s:Envelope>`
67+
}
68+
69+
func GetServicesResponse(host string) string {
70+
return `<?xml version="1.0" encoding="utf-8" standalone="yes"?>
71+
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
72+
<s:Body>
73+
<tds:GetServicesResponse xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
74+
<tds:Service>
75+
<tds:Namespace>http://www.onvif.org/ver10/device/wsdl</tds:Namespace>
76+
<tds:XAddr>http://` + host + `/onvif/device_service</tds:XAddr>
77+
<tds:Version>
78+
<tds:Major>2</tds:Major>
79+
<tds:Minor>5</tds:Minor>
80+
</tds:Version>
81+
</tds:Service>
82+
<tds:Service>
83+
<tds:Namespace>http://www.onvif.org/ver10/media/wsdl</tds:Namespace>
84+
<tds:XAddr>http://` + host + `/onvif/media_service</tds:XAddr>
85+
<tds:Version>
86+
<tds:Major>2</tds:Major>
87+
<tds:Minor>5</tds:Minor>
88+
</tds:Version>
89+
</tds:Service>
90+
</tds:GetServicesResponse>
91+
</s:Body>
6592
</s:Envelope>`
6693
}
6794

@@ -142,7 +169,7 @@ func GetServiceCapabilitiesResponse() string {
142169
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
143170
<s:Body>
144171
<trt:GetServiceCapabilitiesResponse xmlns:trt="http://www.onvif.org/ver10/media/wsdl">
145-
<trt:Capabilities SnapshotUri="false" Rotation="false" VideoSourceMode="false" OSD="false" TemporaryOSDText="false" EXICompression="false">
172+
<trt:Capabilities SnapshotUri="true" Rotation="false" VideoSourceMode="false" OSD="false" TemporaryOSDText="false" EXICompression="false">
146173
<trt:StreamingCapabilities RTPMulticast="false" RTP_TCP="false" RTP_RTSP_TCP="true" NonAggregateControl="false" NoRTSPStreaming="false" />
147174
</trt:Capabilities>
148175
</trt:GetServiceCapabilitiesResponse>
@@ -170,21 +197,55 @@ func GetProfilesResponse(names []string) string {
170197

171198
for i, name := range names {
172199
buf.WriteString(`
173-
<trt:Profiles token="` + name + `" fixed="true">
174-
<tt:Name>` + name + `</tt:Name>
175-
<tt:VideoEncoderConfiguration token="` + strconv.Itoa(i) + `">
176-
<tt:Encoding>H264</tt:Encoding>
177-
<tt:Resolution>
178-
<tt:Width>1920</tt:Width>
179-
<tt:Height>1080</tt:Height>
180-
</tt:Resolution>
181-
</tt:VideoEncoderConfiguration>
182-
</trt:Profiles>`)
200+
<trt:Profiles token="` + name + `" fixed="true">
201+
<trt:Name>` + name + `</trt:Name>
202+
<trt:VideoEncoderConfiguration token="` + strconv.Itoa(i) + `">
203+
<trt:Name>` + name + `</trt:Name>
204+
<trt:Encoding>H264</trt:Encoding>
205+
<trt:Resolution>
206+
<trt:Width>1920</trt:Width>
207+
<trt:Height>1080</trt:Height>
208+
</trt:Resolution>
209+
<trt:RateControl>
210+
</trt:RateControl>
211+
</trt:VideoEncoderConfiguration>
212+
<trt:VideoSourceConfiguration token="` + strconv.Itoa(i) + `">
213+
<trt:Name>` + name + `</trt:Name>
214+
<trt:SourceToken>` + strconv.Itoa(i) + `</trt:SourceToken>
215+
<trt:Bounds x="0" y="0" width="1920" height="1080"></trt:Bounds>
216+
</trt:VideoSourceConfiguration>
217+
</trt:Profiles>`)
183218
}
184219

185220
buf.WriteString(`
186-
</trt:GetProfilesResponse>
187-
</s:Body>
221+
</trt:GetProfilesResponse>
222+
</s:Body>
223+
</s:Envelope>`)
224+
225+
return buf.String()
226+
}
227+
228+
229+
func GetVideoSourcesResponse(names []string) string {
230+
buf := bytes.NewBuffer(nil)
231+
buf.WriteString(`<?xml version="1.0" encoding="utf-8" standalone="yes"?>
232+
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
233+
<s:Body>
234+
<trt:GetVideoSourcesResponse xmlns:trt="http://www.onvif.org/ver10/media/wsdl">`)
235+
236+
for i, _ := range names {
237+
buf.WriteString(`
238+
<trt:VideoSources token="` + strconv.Itoa(i) + `">
239+
<trt:Resolution>
240+
<trt:Width>1920</trt:Width>
241+
<trt:Height>1080</trt:Height>
242+
</trt:Resolution>
243+
</trt:VideoSources>`)
244+
}
245+
246+
buf.WriteString(`
247+
</trt:GetVideoSourcesResponse >
248+
</s:Body>
188249
</s:Envelope>`)
189250

190251
return buf.String()
@@ -196,9 +257,22 @@ func GetStreamUriResponse(uri string) string {
196257
<s:Body>
197258
<trt:GetStreamUriResponse xmlns:trt="http://www.onvif.org/ver10/media/wsdl">
198259
<trt:MediaUri>
199-
<tt:Uri xmlns:tt="http://www.onvif.org/ver10/schema">` + uri + `</tt:Uri>
260+
<trt:Uri>` + uri + `</trt:Uri>
200261
</trt:MediaUri>
201262
</trt:GetStreamUriResponse>
202263
</s:Body>
203264
</s:Envelope>`
204265
}
266+
267+
func GetSnapshotUriResponse(uri string) string {
268+
return `<?xml version="1.0" encoding="utf-8" standalone="yes"?>
269+
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
270+
<s:Body>
271+
<trt:GetSnapshotUriResponse xmlns:trt="http://www.onvif.org/ver10/media/wsdl">
272+
<trt:MediaUri>
273+
<trt:Uri>` + uri + `</trt:Uri>
274+
</trt:MediaUri>
275+
</trt:GetSnapshotUriResponse>
276+
</s:Body>
277+
</s:Envelope>`
278+
}

0 commit comments

Comments
 (0)