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;
}
...
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.
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
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.
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.
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.