c++ffmpeglibavformatrtsp-clientopenrtsp

FFMPEG: 'Nonmatching transport in server reply' But openRTSP works


I bought a cheap chinese ip camera (GWIPC-26xxx/Yoosee). I want to record its stream with ffmpeg.

On FFMPEG I manage to make it work only using the RTSP/UDP transport protocol, like bellow. It also plays flawlessly on VLC.

ffmpeg -rtsp_transport udp -i rtsp://admin:pass@192.168.0.103:554/onvif1 streamfile.mkv

pass is a password defined on the android camera app client.

But I highly prefer the RTSP/TCP transport because using UDP the images are frequently corrupted. Images that appear smeared and teared. So I tested almost anything and even compiled FFMPEG from source using the latest repository. But nothing makes ffmpeg work, android or windows. If use -rtsp_transport tcp I allways end-up receiving:

[rtsp @ 0xxxxxxx] Nonmatching transport in server reply

Finally discovered openRTSP that uses the same library that VLC uses. With it I managed to make it connect using RTSP/TCP (after compiling from source).

openRTSP -n -D 1 -c -B 10000000 -b 10000000 -q -Q -F cam_file \ 
-d 60 -P 30 -t -u admin pass rtsp://192.168.0.103:554/onvif1

More details on openRTSP params here.

The weirdest part is the comparison of the RTSP setup messages (FFMPEG vs openRTSP). It seams clear that the ip camera server supports RTP/AVP/TCP. RTP interleaved into the existing TCP connection.

And looking the source code of ffmpeg/libavformat/rtsp.c it seams ffmpeg is having some problem indentifying it?

...
if (reply->transports[0].lower_transport != lower_transport) {
    av_log(s, AV_LOG_ERROR, "Nonmatching transport in server reply\n");
    err = AVERROR_INVALIDDATA;
    goto fail;
}
...

Solution

  • Quoting Wisdom

    IP cameras are of varying quality, some behaving erratically in my experience. Dealing with their RTSP streams requires a dose of fault-tolerance.
    This appears to be a byproduct of the low-end of the CCTV industry playing fast and loose with standards, RTSP and ONVIF being the two most frequently abused. Fortunately, you can usually work around these problems. Unless your IP cameras and controller are all designed to play nicely together, only use ONVIF for once-only discovery and settings management.

    FFMPEG not very tolerant in RTSP setup

    After struggling I started comparing the RTSP/SETUP messages between openRTSP and ffmpeg. openRTSP by default already outputs a lot of verbose diagnostic.

    openRTSP

    openRTSP sends the commands OPTIONS, DESCRIBE and then SETUP. The SETUP messages were:

    Sending request: SETUP rtsp://192.168.0.103:554/onvif1/track2 RTSP/1.0
    CSeq: 6
    Authorization: Digest username="admin", realm="HIipCamera", nonce="ddd21dbd0620b6fb4b1f9bcbb06340a0", uri="rtsp://192.168.0.103:554/onvif1", response="91d9c611aa004eeb1390b3fbb9373648"
    User-Agent: ./openRTSP (LIVE555 Streaming Media v2021.02.11)
    Transport: RTP/AVP/TCP;unicast;interleaved=2-3
    Session: 3a4d2e6d
    

    response by camera:

    Received a complete SETUP response:
    RTSP/1.0 200 OK
    CSeq: 6
    Transport: RTP/AVP;unicast;destination=192.168.0.100;source=192.168.0.103;interleaved=2-3
    Session: 3a4d2e6d;timeout=60
    

    FFMPEG

    For FFMPEG you have to use -v 9 and -loglevel 99 params to see RTSP messages. It only sent a DESCRIBE request is:

    DESCRIBE rtsp://192.168.0.103:554/onvif1 RTSP/1.0
    Accept: application/sdp
    CSeq: 2
    User-Agent: Lavf58.67.100
    

    response by camera:

    Transport: RTP/AVP;unicast;destination=192.168.0.100;source=192.168.0.103;interleaved=0-1
    Session: 37287775;timeout=60
    

    RTSP abused and FFMPEG hacking-Solution

    Comparing the messages it's clear the camera can connect using RTSP/AVP/TCP interleaved TCP. But we can see by the camera answer that in the 'Transport:' line it doesn't include 'TCP' after 'RTP/AVP' as it was requested. Like:

    Transport: RTP/AVP/('TCP' missing here);....
    

    I analysed the code on and ffmpeg/libavformat/rtsp.c and found the intuitive calling sequence: ff_rtsp_connect, ff_rtsp_make_setup_request, ff_rtsp_send_cmd, ff_rtsp_read_reply and ff_rtsp_parse_line. Inside this last one I found rtsp_parse_transport and the following code:

    if (!av_strcasecmp(lower_transport, "TCP"))
        th->lower_transport = RTSP_LOWER_TRANSPORT_TCP;
    else
        th->lower_transport = RTSP_LOWER_TRANSPORT_UDP;
    

    lower_transport is the text parsed after 'RTP/AVP;' in my case being "" empty char string since the camera server did not include it.

    I insertd || !av_strcasecmp(lower_transport, "") in the code. Making it assume that the transport is RTSP_LOWER_TRANSPORT_TCP when lower_transport is ommited. Like bellow:

    if (!av_strcasecmp(lower_transport, "TCP") || !av_strcasecmp(lower_transport, ""))
        th->lower_transport = RTSP_LOWER_TRANSPORT_TCP;
    else
        th->lower_transport = RTSP_LOWER_TRANSPORT_UDP;
    

    This tiny patch (git) for ffmpeg is available here. Apply using git am < RTSP_lower_transport_TCP.patch on ffmpeg git repo.

    After recompiling: FFMPEG is working perfectly!

    I hacked FFMPEG making it tolerant with the RTSP abuse the camera server was doing. Since my version of FFMPEG will only run on my unused android tv (as a cctv/nvr camera server) this won't create any other problems.

    A better solution would be the FFMPEG (Ticket) consider also the case of a missing lower transport on a rtsp server answer. Then compare with the client-sent request to define whether the lower transport was ommitted. And try to connect with it.

    Advice

    Probably if you reached here your ip camera might be suffering from some RTSP abuse. I recommend you to try using openRTSP first to see if it manages to connect. If so, then try to debug its RTSP/setup messages. It's possible that some custom or hacking solution exists if you modify (at your own risk) the ffmpeg/libavformat/rtsp.c code. Or you might/should use live555 library, VLC or mplayer.