diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index dddaaf42379d86ddfe1289be86ecb185d27559b5..ca9ce7cf21044901af2bc9ede54bfea3f087f41b 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -1291,7 +1291,7 @@ int ff_rtsp_connect(AVFormatContext *s) int port, err, tcp_fd; RTSPMessageHeader reply1 = {0}, *reply = &reply1; int lower_transport_mask = 0; - char real_challenge[64]; + char real_challenge[64] = ""; struct sockaddr_storage peer; socklen_t peer_len = sizeof(peer); @@ -1515,6 +1515,8 @@ redirect: } } while (err); + rt->lower_transport_mask = lower_transport_mask; + av_strlcpy(rt->real_challenge, real_challenge, sizeof(rt->real_challenge)); rt->state = RTSP_STATE_IDLE; rt->seek_timestamp = 0; /* default is to start stream at position zero */ return 0; diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 6dc64b19aea3ffe8efc3ca369efcd882f59b0b3b..62bd3a2eccc9957ddd925e86115c3806e82f882c 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -248,6 +248,9 @@ typedef struct RTSPState { * of RTSPMessageHeader->real_challenge */ enum RTSPServerType server_type; + /** the "RealChallenge1:" field from the server */ + char real_challenge[64]; + /** plaintext authorization line (username:password) */ char auth[128]; @@ -313,6 +316,16 @@ typedef struct RTSPState { /** Filter incoming UDP packets - receive packets only from the right * source address and port. */ int filter_source; + + /** + * A mask with all requested transport methods + */ + int lower_transport_mask; + + /** + * The number of returned packets + */ + uint64_t packets; } RTSPState; /** diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index b910d4cdb97d1519826544c81e3a3b2985ab4d3c..6a4b4af2b3e617193201adda5d5daf2ad8eb0935 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -229,6 +229,20 @@ found: *prtsp_st = rtsp_st; return len; } + +static int resetup_tcp(AVFormatContext *s) +{ + RTSPState *rt = s->priv_data; + char host[1024]; + int port; + + av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, + s->filename); + ff_rtsp_undo_setup(s); + return ff_rtsp_make_setup_request(s, host, port, RTSP_LOWER_TRANSPORT_TCP, + rt->real_challenge); +} + static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) { RTSPState *rt = s->priv_data; @@ -236,6 +250,7 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) RTSPMessageHeader reply1, *reply = &reply1; char cmd[1024]; +retry: if (rt->server_type == RTSP_SERVER_REAL) { int i; @@ -295,8 +310,32 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) } ret = ff_rtsp_fetch_packet(s, pkt); - if (ret < 0) + if (ret < 0) { + if (ret == FF_NETERROR(ETIMEDOUT) && !rt->packets) { + if (rt->lower_transport == RTSP_LOWER_TRANSPORT_UDP && + rt->lower_transport_mask & (1 << RTSP_LOWER_TRANSPORT_TCP)) { + RTSPMessageHeader reply1, *reply = &reply1; + av_log(s, AV_LOG_WARNING, "UDP timeout, retrying with TCP\n"); + if (rtsp_read_pause(s) != 0) + return -1; + // TEARDOWN is required on Real-RTSP, but might make + // other servers close the connection. + if (rt->server_type == RTSP_SERVER_REAL) + ff_rtsp_send_cmd(s, "TEARDOWN", rt->control_uri, NULL, + reply, NULL); + rt->session_id[0] = '\0'; + if (resetup_tcp(s) == 0) { + rt->state = RTSP_STATE_IDLE; + rt->need_subscription = 1; + if (rtsp_read_play(s) != 0) + return -1; + goto retry; + } + } + } return ret; + } + rt->packets++; /* send dummy request to keep TCP connection alive */ if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2) {