Skip to content

ONVIF:Example:FindDevice

네트워크 상의 카메라를 ONVIF로 찾아내고 통신하는 방법을 정리한다. 코드는 Python을 사용.

ONVIF 공식 문서는 ONVIF 어플리케이션 개발자 가이드를 참조.1

ONVIFWSDL 스팩은 Network Interface Specifications - ONVIF 또는 Github - onvif/specs 페이지에서 받을 수 있다.

Device Discovery

장치가 네트워크 상에 존재하는지 확인해야 한다. 이 때 사용하는 기술 스팩은 WS-Discovery이며, Python 라이브러리는 WSDiscovery를 사용한다.

TCP 및 UDP 포트 3702를 통해 작동 하며 IP 멀티 캐스트 주소 239.255.255.250 or FF02::C를 사용, 실제 통신은 SOAP-over-UDP을 사용하여 수행.

코드는 다음과 같다:

from wsdiscovery.discovery import ThreadedWSDiscovery as WSDiscovery

wsd = WSDiscovery()
wsd.start()
services = wsd.searchServices()
for service in services:
    for address in service.getXAddrs():
        print(service.getEPR() + ":" + address)

다음과 같이 출력된다.

urn:uuid:03590000-e4f9-11b4-8389-5803fb986fcf:http://192.168.0.201/onvif/device_service
urn:uuid:03590000-e4f9-11b4-8389-5803fb986fcf:http://[fe80::5a03:fbff:fe98:6fcf]/onvif/device_service
urn:uuid:78c2c020-78c2-4c02-801d-78c2c0201d75:http://192.168.0.50/onvif/device_service
urn:uuid:594333b6-5750-4471-b3aa-bec2042940c6:http://192.168.0.61:5357/594333b6-5750-4471-b3aa-bec2042940c6/

장비의 UUID와 WSDL을 위한 주소를 기억하자.

WSDL Device Management

WSDL의 장치 관리자를 통해 장치의 여러 정보를 획득한다. 이 것은 SOAP으로 작동한다. zeep 라이브러리를 사용한다.

from zeep import Client
wsdl = "http://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl"
client = Client(wsdl=wsdl)
service = client.create_service("{http://www.onvif.org/ver10/device/wsdl}DeviceBinding", "http://192.168.0.50/onvif/device_service")
print(service.GetCapabilities())

참고로 Client.create_service에 들어갈 인자들은 zeep#Client.create_service 항목을 참고하면 된다.

WSDL 스팩 (devicemgmt.wsdl)을 통해 장치의 정보를 획득할 수 있다.

중요한 내용은, GetCapabilities() SOAP API 를 통해 Media 관련 정보를 획득할 수 있다.

다음과 같이 출력된다.

{
    ...
    'Media': {
        'XAddr': 'http://192.168.0.50/onvif/device_service',
        'StreamingCapabilities': {
            'RTPMulticast': True,
            'RTP_TCP': True,
            'RTP_RTSP_TCP': True,
            'Extension': None,
            '_attr_1': None
        },
        '_value_1': None,
        'Extension': None,
        '_attr_1': None
    },
    ...

위의 XAddrStreamingCapabilities를 기억하자. 마지막, 스트림 주소 획득시 필요하다.

미디어 프로파일 정보 획득

media.wsdl 스팩을 통해 미디어 정보를 획득하자. 필요하다면 UsernameToken, HTTPBasicAuth, HTTPDigestAuth 등 을 포함시켜 인증 정보를 추가하자.

from zeep import Client
from zeep.wsse.username import UsernameToken

wsdl = "http://www.onvif.org/ver10/media/wsdl/media.wsdl"
client = Client(wsdl=wsdl, wsse=UsernameToken("admin", "0000", use_digest=True))
service = client.create_service("{http://www.onvif.org/ver10/media/wsdl}MediaBinding", "http://192.168.0.50/onvif/device_service")
profiles = service.GetProfiles()
print(profiles)

프로파일은 다음과 같이 출력된다:

[{
    'Name': 'Profile1',
    'VideoSourceConfiguration': {
        ...
    },
    'token': 'Profile1',
...

스트리밍을 위한 프로파일의 토큰 이름을 기억하자. 위에선, "Profile1" 이다.

참고로 wsse 는 pre-OASIS 버전을 나타내는 키워드.

스트림 URI 획득

위의 토큰 이름을 취득한후, 다음과 같이 스트림의 URI 를 획득하면 된다.

factory = client.type_factory("http://www.onvif.org/ver10/schema")
transport = factory.Transport(Protocol='RTSP')
setup = factory.StreamSetup(Stream='RTP-Unicast', Transport=transport)
result = service.GetStreamUri(StreamSetup=setup, ProfileToken='Profile1')
print(result)

결과는 다음과 같이 출력된다:

{
    'Uri': 'rtsp://192.168.0.50:554/media/1/1/Profile1',
    'InvalidAfterConnect': False,
    'InvalidAfterReboot': True,
    'Timeout': datetime.timedelta(0),
    '_value_1': None,
    '_attr_1': None
}

Transport 의 Protocol 목록은 다음과 같다:

    <xs:simpleType name="TransportProtocol">
        <xs:restriction base="xs:string">
            <xs:enumeration value="UDP"/>
            <xs:enumeration value="TCP"/>
            <xs:enumeration value="RTSP"/>
            <xs:enumeration value="HTTP"/>
        </xs:restriction>
    </xs:simpleType>

StreamSetup 의 Stream 목록은 다음과 같다:

    <xs:simpleType name="StreamType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="RTP-Unicast"/>
            <xs:enumeration value="RTP-Multicast"/>
        </xs:restriction>
    </xs:simpleType>

스냅샷 획득

프로파일 토큰을 사용하여 GetSnapshotUri를 호출하면 된다.

service.GetSnapshotUri(ProfileToken='Profile1')

다음의 객체가 획득된다.

{
    'Uri': 'http://192.168.0.50/snapshot/1/snapshot.jpg',
    'InvalidAfterConnect': False,
    'InvalidAfterReboot': False,
    'Timeout': datetime.timedelta(0),
    '_value_1': None,
    '_attr_1': None
}

위의 URI 주소로 들어가면 된다.

코드 샘플 참조

See also

Favorite site

References


  1. 7.1 Using an Existing Profile for Media Streaming 항목을 보면 된다 

  2. ONVIF_and_SOAP_and_web_service.pdf 

  3. ONVIF_WG-APG-Application_Programmers_Guide-1.pdf