Skip to content

Instantly share code, notes, and snippets.

@schauveau
Last active January 27, 2024 08:31
Show Gist options
  • Save schauveau/c4ff736ae46f9d21527ef1eccee5baa1 to your computer and use it in GitHub Desktop.
Save schauveau/c4ff736ae46f9d21527ef1eccee5baa1 to your computer and use it in GitHub Desktop.
Notes about TP-Link Tapo C500 and Linux

TP-Link Tapo C500 and Linux

Those are my notes about using the TP-Link Tapo C500 camera in my Linux environment.

More precisely, my Linux system are

  • a Debian Testing desktop pc
  • a few raspberry pi
  • an Asustor NAS that is running a highly customized Linux

Tapo C500 technical summary

RTSP Streaming

The camera is using the default rtsp port (554) and provides 2 profiles accessible with the followng URLS

  • rtsp://username:password@camera_ip/stream1
    • The resolution can be 1920x1024, 1280x720 or 640x360
    • As far as know, there is no way to specify it in the URL.
    • It can only be done via Onvif or the Android app
  • rtsp://username:password@camera_ip/stream2
    • This is always 640x360

nmap

nmap reports the following

PORT     STATE SERVICE
443/tcp  open  https
554/tcp  open  rtsp
1024/tcp open  kdm                  (can also be 1025) 
2020/tcp open  xinupageserver
8800/tcp open  sunwebadmin

Port 443 (https)

That port can be opened with a web browser or with curl -v -k https:\\camera_ip but produces an error 404.

That could be a hidden web interface or something related to the HTTP transport mode of RTSP

TO BE INVESTIGATED.

Port 554 (rtsp)

This is the default RTSP port so nothing unusual.

Port 1024 or 1025

The port number changes over time.

This is probably used to sent notifications event to the Onvif clients.

See also port 8800 below.

Port 2020

This is the ONVIF port (non-standard)

Port 8800

After opening it with nc camera_ic 8800 I once got the following reply:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsdd="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:chan="http://schemas.microsoft.com/ws/2005/02/duplex" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:xmime="http://tempuri.org/xmime.xsd" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:wsrfbf="http://docs.oasis-open.org/wsrf/bf-2" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:wsrfr="http://docs.oasis-open.org/wsrf/r-2" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:tns1="http://www.onvif.org/ver10/topics" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:tdn="http://www.onvif.org/ver10/network/wsdl" xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:trp="http://www.onvif.org/ver10/replay/wsdl" xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:hikwsd="http://www.onvifext.com/onvif/ext/ver10/wsdl" xmlns:hikxsd="http://www.onvifext.com/onvif/ext/ver10/schema">
  <SOAP-ENV:Header>
    <wsa:MessageID>urn:uuid:388f2f01-57ac-4929-9a8f-5e0319e3cff8</wsa:MessageID>
    <wsa:To SOAP-ENV:mustUnderstand="true">http://192.168.1.26:1024/event-1024_1024</wsa:To>
    <wsa:Action SOAP-ENV:mustUnderstand="true">http://www.onvif.org/ver10/events/wsdl/PullPointSubscription/PullMessagesRequest</wsa:Action>
    <wsa5:Action SOAP-ENV:mustUnderstand="true">http://www.onvif.org/ver10/events/wsdl/PullPointSubscription/PullMessagesResponse</wsa5:Action>
  </SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <tev:PullMessagesResponse>
      <tev:CurrentTime>2024-01-26T10:33:08Z</tev:CurrentTime>
      <tev:TerminationTime>2024-01-26T10:34:08Z</tev:TerminationTime>
    </tev:PullMessagesResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

That message is obviously related to the event mechanism (e.g. a movement was detected) of ONVIF.

The URL on the 4 line also tells me that port 1024 is used to send those events.

TODO: Check for port 8800 in the ONVIF description of the available services.

Display with mpv

mpv rtsp://username:password@camera_ip/stream1 --profile=low-latency --untimed

The two options --profile=low-latency --untimed reduces the latency from about 3s to 1s.

See

Display with ffplay

ffplay rtsp://username:password@camera_ip/stream1 -rtsp_transport tcp -flags +low_delay -fflags +nobuffer

The option -fflags +nobuffer reduces the latency from about 3s to less than 1s.

If the option -rtsp_transport tcp is not set, then ffplay is probably using UDP and the bottom of the image is usually corrupted.

https://www.reddit.com/r/ffmpeg/comments/u741n7/remove_delay_on_ffplay_for_capture_device/

Record with ffmpeg

Using onvif-gui, onvif-util and libonvif1

See https://github.com/sr99622/libonvif

On Debian, use apt install onvif-tools libonvif1 libonvif-dev but be aware that this is version 1.4.2.

In the current github trunk, onvif-gui was rewritten from C++ to Python.

TODO: install and test the latest github version.

onvif-gui (1.4.2 from Debian)

A simple tool to control Onvif camera.

Press the discovery button (near the bottom right) and double click on a discovered camera to enable streaming.

Can be used to change the resolution of stream1, move the camera (PTZ) and do a lot of other things.

Unfortunately, the output is often corrupted probably because it uses UDP transport.

TODO: Fill a ticket to request TCP (but test the trunk first)

Stream as HSL m3u8 using ffmpeg

TO BE WRITTEN

Simple ONVIF queries using curl

ONVIF is a typical http based protocol using POST queries.

The protocol used to pass the password is quite complex but a few queries do not require authentication.

For instance, the GetCapabilities can performed as follow

Create a file request.xml such as

<?xml version=1.0' encoding='utf-8'>
<soap-env:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <soap-env:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <GetCapabilities xmlns="http://www.onvif.org/ver10/device/wsdl"></GetCapabilities>
    </soap-env:Body>
</soap-env:Envelope>

and then

curl -s -S -k -X POST --header 'Content-Type: text/xml; charset=utf-8' -d @request.xml http://192.168.1.26:2020/onvif/service 

You may want to pipe the output in xmllint - -format to make it more readable.

Similarly, the GetServices query provides a shorter list of the supported services.

Analysis of GetCapabilities and GetServices responses

GetServices Response

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsdd="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:chan="http://schemas.microsoft.com/ws/2005/02/duplex" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:xmime="http://tempuri.org/xmime.xsd" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:wsrfbf="http://docs.oasis-open.org/wsrf/bf-2" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:wsrfr="http://docs.oasis-open.org/wsrf/r-2" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:tns1="http://www.onvif.org/ver10/topics" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:tdn="http://www.onvif.org/ver10/network/wsdl" xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:trp="http://www.onvif.org/ver10/replay/wsdl" xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:hikwsd="http://www.onvifext.com/onvif/ext/ver10/wsdl" xmlns:hikxsd="http://www.onvifext.com/onvif/ext/ver10/schema">
  <SOAP-ENV:Body>
    <tds:GetServicesResponse>
      <tds:Service>
        <tds:Namespace>http://www.onvif.org/ver10/device/wsdl</tds:Namespace>
        <tds:XAddr>http://192.168.1.26:2020/onvif/service</tds:XAddr>
        <tds:Version>
          <tt:Major>2</tt:Major>
          <tt:Minor>20</tt:Minor>
        </tds:Version>
      </tds:Service>
      <tds:Service>
        <tds:Namespace>http://www.onvif.org/ver10/media/wsdl</tds:Namespace>
        <tds:XAddr>http://192.168.1.26:2020/onvif/service</tds:XAddr>
        <tds:Version>
          <tt:Major>2</tt:Major>
          <tt:Minor>20</tt:Minor>
        </tds:Version>
      </tds:Service>
      <tds:Service>
        <tds:Namespace>http://www.onvif.org/ver10/events/wsdl</tds:Namespace>
        <tds:XAddr>http://192.168.1.26:2020/onvif/service</tds:XAddr>
        <tds:Version>
          <tt:Major>2</tt:Major>
          <tt:Minor>20</tt:Minor>
        </tds:Version>
      </tds:Service>
      <tds:Service>
        <tds:Namespace>http://www.onvif.org/ver20/analytics/wsdl</tds:Namespace>
        <tds:XAddr>http://192.168.1.26:2020/onvif/service</tds:XAddr>
        <tds:Version>
          <tt:Major>2</tt:Major>
          <tt:Minor>20</tt:Minor>
        </tds:Version>
      </tds:Service>
      <tds:Service>
        <tds:Namespace>http://www.onvif.org/ver20/imaging/wsdl</tds:Namespace>
        <tds:XAddr>http://192.168.1.26:2020/onvif/service</tds:XAddr>
        <tds:Version>
          <tt:Major>2</tt:Major>
          <tt:Minor>20</tt:Minor>
        </tds:Version>
      </tds:Service>
      <tds:Service>
        <tds:Namespace>http://www.onvif.org/ver20/ptz/wsdl</tds:Namespace>
        <tds:XAddr>http://192.168.1.26:2020/onvif/service</tds:XAddr>
        <tds:Version>
          <tt:Major>2</tt:Major>
          <tt:Minor>20</tt:Minor>
        </tds:Version>
      </tds:Service>
      <tds:Service>
        <tds:Namespace>http://www.onvif.org/ver10/deviceIO/wsdl</tds:Namespace>
        <tds:XAddr>http://192.168.1.26:2020/onvif/service</tds:XAddr>
        <tds:Version>
          <tt:Major>2</tt:Major>
          <tt:Minor>20</tt:Minor>
        </tds:Version>
      </tds:Service>
    </tds:GetServicesResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The supported services are device, media, events, analytics, imaging, ptz and deviceIO. See https://www.onvif.org/specs/DocMap.html for their respective specifications.

  • device is the core service.
  • media is about controlling the video and audio streams. This is where you can change the resolution of stream1.
  • events is about the event notifications (e.g. a movement was detected, camera tempering, ...)
  • analytics is about the image analysis (e.g. detect persons, track movements, ...)
  • imaging is about the image and camera settings (e.g. brightness, focus, ...)
  • ptz is about Pan, Tilt and Zoom
  • deviceIO is about the input and output audio, video and relay.

What is obviously missing is the recording service. The Tapo android app must be using an extension to access the sd-card content.

Each service should have a GetServiceCapabilities or something similar to get more details but that requires authentication.

However, the GetCapabilities query gives some details about each service without auth. See Below.

GetCapabilities Response

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" 
                   xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" 
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
                   xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" 
                   xmlns:wsdd="http://schemas.xmlsoap.org/ws/2005/04/discovery" 
                   xmlns:chan="http://schemas.microsoft.com/ws/2005/02/duplex" 
                   xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
                   xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
                   xmlns:wsa5="http://www.w3.org/2005/08/addressing"
                   xmlns:xmime="http://tempuri.org/xmime.xsd"
                   xmlns:xop="http://www.w3.org/2004/08/xop/include" 
                   xmlns:wsrfbf="http://docs.oasis-open.org/wsrf/bf-2" 
                   xmlns:wstop="http://docs.oasis-open.org/wsn/t-1"
                   xmlns:wsrfr="http://docs.oasis-open.org/wsrf/r-2"
                   xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2"
                   xmlns:tt="http://www.onvif.org/ver10/schema" 
                   xmlns:ter="http://www.onvif.org/ver10/error" 
                   xmlns:tns1="http://www.onvif.org/ver10/topics" 
                   xmlns:tds="http://www.onvif.org/ver10/device/wsdl"
                   xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl" 
                   xmlns:trt="http://www.onvif.org/ver10/media/wsdl" 
                   xmlns:tev="http://www.onvif.org/ver10/events/wsdl"
                   xmlns:tdn="http://www.onvif.org/ver10/network/wsdl"
                   xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" 
                   xmlns:trp="http://www.onvif.org/ver10/replay/wsdl"
                   xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl"
                   xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl"
                   xmlns:hikwsd="http://www.onvifext.com/onvif/ext/ver10/wsdl" 
                   xmlns:hikxsd="http://www.onvifext.com/onvif/ext/ver10/schema">
  <SOAP-ENV:Body>
    <tds:GetCapabilitiesResponse>
      <tds:Capabilities>
        <tt:Analytics>
          <tt:XAddr>http://192.168.1.26:2020/onvif/service</tt:XAddr>
          <tt:RuleSupport>true</tt:RuleSupport>
          <tt:AnalyticsModuleSupport>true</tt:AnalyticsModuleSupport>
        </tt:Analytics>
        <tt:Device>
          <tt:XAddr>http://192.168.1.26:2020/onvif/service</tt:XAddr>
          <tt:Network>
            <tt:IPFilter>false</tt:IPFilter>
            <tt:ZeroConfiguration>false</tt:ZeroConfiguration>
            <tt:IPVersion6>false</tt:IPVersion6>
            <tt:DynDNS>false</tt:DynDNS>
            <tt:Extension>
              <tt:Dot11Configuration>true</tt:Dot11Configuration>
            </tt:Extension>
          </tt:Network>
          <tt:System>
            <tt:DiscoveryResolve>false</tt:DiscoveryResolve>
            <tt:DiscoveryBye>true</tt:DiscoveryBye>
            <tt:RemoteDiscovery>false</tt:RemoteDiscovery>
            <tt:SystemBackup>false</tt:SystemBackup>
            <tt:SystemLogging>false</tt:SystemLogging>
            <tt:FirmwareUpgrade>false</tt:FirmwareUpgrade>
            <tt:SupportedVersions>
              <tt:Major>20</tt:Major>
              <tt:Minor>6</tt:Minor>
            </tt:SupportedVersions>
            <tt:Extension>
              <tt:HttpFirmwareUpgrade>false</tt:HttpFirmwareUpgrade>
              <tt:HttpSystemBackup>false</tt:HttpSystemBackup>
              <tt:HttpSystemLogging>false</tt:HttpSystemLogging>
              <tt:HttpSupportInformation>false</tt:HttpSupportInformation>
            </tt:Extension>
          </tt:System>
          <tt:IO/>
          <tt:Security>
            <tt:TLS1.1>false</tt:TLS1.1>
            <tt:TLS1.2>false</tt:TLS1.2>
            <tt:OnboardKeyGeneration>false</tt:OnboardKeyGeneration>
            <tt:AccessPolicyConfig>false</tt:AccessPolicyConfig>
            <tt:X.509Token>false</tt:X.509Token>
            <tt:SAMLToken>false</tt:SAMLToken>
            <tt:KerberosToken>false</tt:KerberosToken>
            <tt:RELToken>false</tt:RELToken>
          </tt:Security>
        </tt:Device>
        <tt:Events>
          <tt:XAddr>http://192.168.1.26:2020/onvif/service</tt:XAddr>
          <tt:WSSubscriptionPolicySupport>true</tt:WSSubscriptionPolicySupport>
          <tt:WSPullPointSupport>true</tt:WSPullPointSupport>
          <tt:WSPausableSubscriptionManagerInterfaceSupport>false</tt:WSPausableSubscriptionManagerInterfaceSupport>
        </tt:Events>
        <tt:Imaging>
          <tt:XAddr>http://192.168.1.26:2020/onvif/service</tt:XAddr>
        </tt:Imaging>
        <tt:Media>
          <tt:XAddr>http://192.168.1.26:2020/onvif/service</tt:XAddr>
          <tt:StreamingCapabilities>
            <tt:RTPMulticast>false</tt:RTPMulticast>
            <tt:RTP_TCP>true</tt:RTP_TCP>
            <tt:RTP_RTSP_TCP>true</tt:RTP_RTSP_TCP>
          </tt:StreamingCapabilities>
        </tt:Media>
        <tt:PTZ>
          <tt:XAddr>http://192.168.1.26:2020/onvif/service</tt:XAddr>
        </tt:PTZ>
        <tt:Extension>
          <tt:DeviceIO>
            <tt:XAddr/>
            <tt:VideoSources>0</tt:VideoSources>
            <tt:VideoOutputs>0</tt:VideoOutputs>
            <tt:AudioSources>1</tt:AudioSources>
            <tt:AudioOutputs>1</tt:AudioOutputs>
            <tt:RelayOutputs>0</tt:RelayOutputs>
          </tt:DeviceIO>
          <hikxsd:hikCapabilities>
            <hikxsd:XAddr>http://192.168.1.26:2020/onvif/service</hikxsd:XAddr>
            <hikxsd:PTZ3DZoomSupport>true</hikxsd:PTZ3DZoomSupport>
            <hikxsd:PTZPatternSupport>false</hikxsd:PTZPatternSupport>
            <hikxsd:IOInputSupport>false</hikxsd:IOInputSupport>
            <hikxsd:PrivacyMaskSupport>false</hikxsd:PrivacyMaskSupport>
            <hikxsd:Language>2</hikxsd:Language>
          </hikxsd:hikCapabilities>
        </tt:Extension>
      </tds:Capabilities>
    </tds:GetCapabilitiesResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

In Media/StreamingCapability we have RTPMulticast (i,e. RTSP over UDP), RTP_TCP and RTP_RTSP_TCP so http and websocket are obviously not supported. See also section 5.1.1.3 of https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec.pdf

Device/Network/IPVersion6 is false so no IPv6

There are a few extensions but I do not see anything that could be related to recording or to SD card storage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment