X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtsp.c;h=bdbc7ebb741462a25db5c9e96281b03835ba2b02;hb=5fe8021a6a93c9a0ab46e0e913649aa2b8f32d5d;hp=6507841cdb514d8f5b956724162effef8bf5d5a0;hpb=d3f84dfc0e65201983e88c99b7544d5a60d0165e;p=ffmpeg diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 6507841cdb5..bdbc7ebb741 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -38,7 +38,7 @@ #include "rtpdec.h" #include "rdt.h" -#include "rtpdec_asf.h" +#include "rtpdec_formats.h" //#define DEBUG //#define DEBUG_RTP_TCP @@ -52,19 +52,8 @@ int rtsp_default_protocols = (1 << RTSP_LOWER_TRANSPORT_UDP); #define SELECT_TIMEOUT_MS 100 #define READ_PACKET_TIMEOUT_S 10 #define MAX_TIMEOUTS READ_PACKET_TIMEOUT_S * 1000 / SELECT_TIMEOUT_MS - -#define SPACE_CHARS " \t\r\n" -/* we use memchr() instead of strchr() here because strchr() will return - * the terminating '\0' of SPACE_CHARS instead of NULL if c is '\0'. */ -#define redir_isspace(c) memchr(SPACE_CHARS, c, 4) -static void skip_spaces(const char **pp) -{ - const char *p; - p = *pp; - while (redir_isspace(*p)) - p++; - *pp = p; -} +#define SDP_MAX_SIZE 16384 +#define RECVBUF_SIZE 10 * RTP_MAX_PACKET_LENGTH static void get_word_until_chars(char *buf, int buf_size, const char *sep, const char **pp) @@ -73,7 +62,7 @@ static void get_word_until_chars(char *buf, int buf_size, char *q; p = *pp; - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); q = buf; while (!strchr(sep, *p) && *p != '\0') { if ((q - buf) < buf_size - 1) @@ -126,6 +115,12 @@ static int sdp_parse_rtpmap(AVFormatContext *s, break; } } + /* If no dynamic handler was found, check with the list of standard + * allocated types, if such a stream for some reason happens to + * use a private payload type. This isn't handled in rtpdec.c, since + * the format name from the rtpmap line never is passed into rtpdec. */ + if (!rtsp_st->dynamic_handler) + codec->codec_id = ff_rtp_codec_id(buf, codec->codec_type); } else { /* We are in a standard case * (from http://www.iana.org/assignments/rtp-parameters). */ @@ -171,92 +166,13 @@ static int sdp_parse_rtpmap(AVFormatContext *s, return 0; } -/* return the length and optionally the data */ -static int hex_to_data(uint8_t *data, const char *p) -{ - int c, len, v; - - len = 0; - v = 1; - for (;;) { - skip_spaces(&p); - if (*p == '\0') - break; - c = toupper((unsigned char) *p++); - if (c >= '0' && c <= '9') - c = c - '0'; - else if (c >= 'A' && c <= 'F') - c = c - 'A' + 10; - else - break; - v = (v << 4) | c; - if (v & 0x100) { - if (data) - data[len] = v; - len++; - v = 1; - } - } - return len; -} - -static void sdp_parse_fmtp_config(AVCodecContext * codec, void *ctx, - char *attr, char *value) -{ - switch (codec->codec_id) { - case CODEC_ID_MPEG4: - case CODEC_ID_AAC: - if (!strcmp(attr, "config")) { - /* decode the hexa encoded parameter */ - int len = hex_to_data(NULL, value); - if (codec->extradata) - av_free(codec->extradata); - codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); - if (!codec->extradata) - return; - codec->extradata_size = len; - hex_to_data(codec->extradata, value); - } - break; - default: - break; - } - return; -} - -typedef struct { - const char *str; - uint16_t type; - uint32_t offset; -} AttrNameMap; - -/* All known fmtp parameters and the corresponding RTPAttrTypeEnum */ -#define ATTR_NAME_TYPE_INT 0 -#define ATTR_NAME_TYPE_STR 1 -static const AttrNameMap attr_names[]= -{ - { "SizeLength", ATTR_NAME_TYPE_INT, - offsetof(RTPPayloadData, sizelength) }, - { "IndexLength", ATTR_NAME_TYPE_INT, - offsetof(RTPPayloadData, indexlength) }, - { "IndexDeltaLength", ATTR_NAME_TYPE_INT, - offsetof(RTPPayloadData, indexdeltalength) }, - { "profile-level-id", ATTR_NAME_TYPE_INT, - offsetof(RTPPayloadData, profile_level_id) }, - { "StreamType", ATTR_NAME_TYPE_INT, - offsetof(RTPPayloadData, streamtype) }, - { "mode", ATTR_NAME_TYPE_STR, - offsetof(RTPPayloadData, mode) }, - { NULL, -1, -1 }, -}; - /* parse the attribute line from the fmtp a line of an sdp response. This * is broken out as a function because it is used in rtp_h264.c, which is * forthcoming. */ int ff_rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, char *value, int value_size) { - skip_spaces(p); + *p += strspn(*p, SPACE_CHARS); if (**p) { get_word_sep(attr, attr_size, "=", p); if (**p == '=') @@ -269,37 +185,6 @@ int ff_rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, return 0; } -/* parse a SDP line and save stream attributes */ -static void sdp_parse_fmtp(AVStream *st, const char *p) -{ - char attr[256]; - char value[4096]; - int i; - RTSPStream *rtsp_st = st->priv_data; - AVCodecContext *codec = st->codec; - RTPPayloadData *rtp_payload_data = &rtsp_st->rtp_payload_data; - - /* loop on each attribute */ - while (ff_rtsp_next_attr_and_value(&p, attr, sizeof(attr), - value, sizeof(value))) { - /* grab the codec extra_data from the config parameter of the fmtp - * line */ - sdp_parse_fmtp_config(codec, rtsp_st->dynamic_protocol_context, - attr, value); - /* Looking for a known attribute */ - for (i = 0; attr_names[i].str; ++i) { - if (!strcasecmp(attr, attr_names[i].str)) { - if (attr_names[i].type == ATTR_NAME_TYPE_INT) { - *(int *)((char *)rtp_payload_data + - attr_names[i].offset) = atoi(value); - } else if (attr_names[i].type == ATTR_NAME_TYPE_STR) - *(char **)((char *)rtp_payload_data + - attr_names[i].offset) = av_strdup(value); - } - } - } -} - /** Parse a string p in the form of Range:npt=xx-xx, and determine the start * and end time. * Used for seeking in the rtp stream. @@ -308,7 +193,7 @@ static void rtsp_parse_range_npt(const char *p, int64_t *start, int64_t *end) { char buf[256]; - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); if (!av_stristart(p, "npt=", &p)) return; @@ -326,9 +211,21 @@ static void rtsp_parse_range_npt(const char *p, int64_t *start, int64_t *end) // av_log(NULL, AV_LOG_DEBUG, "Range End: %lld\n", *end); } +static int get_sockaddr(const char *buf, struct sockaddr_storage *sock) +{ + struct addrinfo hints, *ai = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(buf, NULL, &hints, &ai)) + return -1; + memcpy(sock, ai->ai_addr, FFMIN(sizeof(*sock), ai->ai_addrlen)); + freeaddrinfo(ai); + return 0; +} + typedef struct SDPParseState { /* SDP only */ - struct in_addr default_ip; + struct sockaddr_storage default_ip; int default_ttl; int skip_media; ///< set if an unknown m= line occurs } SDPParseState; @@ -343,7 +240,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, int payload_type, i; AVStream *st; RTSPStream *rtsp_st; - struct in_addr sdp_ip; + struct sockaddr_storage sdp_ip; int ttl; dprintf(s, "sdp: %c='%s'\n", letter, buf); @@ -357,10 +254,10 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, if (strcmp(buf1, "IN") != 0) return; get_word(buf1, sizeof(buf1), &p); - if (strcmp(buf1, "IP4") != 0) + if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6")) return; get_word_sep(buf1, sizeof(buf1), "/", &p); - if (ff_inet_aton(buf1, &sdp_ip) == 0) + if (get_sockaddr(buf1, &sdp_ip)) return; ttl = 16; if (*p == '/') { @@ -450,7 +347,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, rtsp_st = st->priv_data; /* XXX: may need to add full url resolution */ - ff_url_split(proto, sizeof(proto), NULL, 0, NULL, 0, + av_url_split(proto, sizeof(proto), NULL, 0, NULL, 0, NULL, NULL, 0, p); if (proto[0] == '\0') { /* relative control URL */ @@ -470,22 +367,9 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, st = s->streams[s->nb_streams - 1]; rtsp_st = st->priv_data; sdp_parse_rtpmap(s, st->codec, rtsp_st, payload_type, p); - } else if (av_strstart(p, "fmtp:", &p)) { + } else if (av_strstart(p, "fmtp:", &p) || + av_strstart(p, "framesize:", &p)) { /* NOTE: fmtp is only supported AFTER the 'a=rtpmap:xxx' tag */ - get_word(buf1, sizeof(buf1), &p); - payload_type = atoi(buf1); - for (i = 0; i < s->nb_streams; i++) { - st = s->streams[i]; - rtsp_st = st->priv_data; - if (rtsp_st->sdp_payload_type == payload_type) { - if (!(rtsp_st->dynamic_handler && - rtsp_st->dynamic_handler->parse_sdp_a_line && - rtsp_st->dynamic_handler->parse_sdp_a_line(s, - i, rtsp_st->dynamic_protocol_context, buf))) - sdp_parse_fmtp(st, p); - } - } - } else if (av_strstart(p, "framesize:", &p)) { // let dynamic protocol handlers have a stab at the line. get_word(buf1, sizeof(buf1), &p); payload_type = atoi(buf1); @@ -547,7 +431,7 @@ static int sdp_parse(AVFormatContext *s, const char *content) memset(s1, 0, sizeof(SDPParseState)); p = content; for (;;) { - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); letter = *p; if (letter == '\0') break; @@ -615,6 +499,7 @@ void ff_rtsp_close_streams(AVFormatContext *s) av_close_input_stream (rt->asf_ctx); rt->asf_ctx = NULL; } + av_free(rt->recvbuf); } static void *rtsp_rtp_mux_open(AVFormatContext *s, AVStream *st, @@ -698,7 +583,8 @@ static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) else rtsp_st->transport_priv = rtp_parse_open(s, st, rtsp_st->rtp_handle, rtsp_st->sdp_payload_type, - &rtsp_st->rtp_payload_data); + (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP || !s->max_delay) + ? 0 : RTP_REORDER_QUEUE_DEFAULT_SIZE); if (!rtsp_st->transport_priv) { return AVERROR(ENOMEM); @@ -727,7 +613,7 @@ static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp) int v; p = *pp; - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); v = strtol(p, (char **)&p, 10); if (*p == '-') { p++; @@ -754,7 +640,7 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) reply->nb_transports = 0; for (;;) { - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); if (*p == '\0') break; @@ -820,15 +706,19 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) th->ttl = strtol(p, (char **)&p, 10); } } else if (!strcmp(parameter, "destination")) { - struct in_addr ipaddr; - if (*p == '=') { p++; get_word_sep(buf, sizeof(buf), ";,", &p); - if (ff_inet_aton(buf, &ipaddr)) - th->destination = ntohl(ipaddr.s_addr); + get_sockaddr(buf, &th->destination); + } + } else if (!strcmp(parameter, "source")) { + if (*p == '=') { + p++; + get_word_sep(buf, sizeof(buf), ";,", &p); + av_strlcpy(th->source, buf, sizeof(th->source)); } } + while (*p != ';' && *p != '\0' && *p != ',') p++; if (*p == ';') @@ -864,22 +754,22 @@ void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf, } else if (av_stristart(p, "Range:", &p)) { rtsp_parse_range_npt(p, &reply->range_start, &reply->range_end); } else if (av_stristart(p, "RealChallenge1:", &p)) { - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); av_strlcpy(reply->real_challenge, p, sizeof(reply->real_challenge)); } else if (av_stristart(p, "Server:", &p)) { - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); av_strlcpy(reply->server, p, sizeof(reply->server)); } else if (av_stristart(p, "Notice:", &p) || av_stristart(p, "X-Notice:", &p)) { reply->notice = strtol(p, NULL, 10); } else if (av_stristart(p, "Location:", &p)) { - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); av_strlcpy(reply->location, p , sizeof(reply->location)); } else if (av_stristart(p, "WWW-Authenticate:", &p) && auth_state) { - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); ff_http_auth_handle_header(auth_state, "WWW-Authenticate", p); } else if (av_stristart(p, "Authentication-Info:", &p) && auth_state) { - skip_spaces(&p); + p += strspn(p, SPACE_CHARS); ff_http_auth_handle_header(auth_state, "Authentication-Info", p); } } @@ -933,7 +823,7 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, dprintf(s, "ret=%d c=%02x [%c]\n", ret, ch, ch); #endif if (ret != 1) - return -1; + return AVERROR_EOF; if (ch == '\n') break; if (ch == '$') { @@ -960,6 +850,7 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, get_word(buf1, sizeof(buf1), &p); get_word(buf1, sizeof(buf1), &p); reply->status_code = atoi(buf1); + av_strlcpy(reply->reason, p, sizeof(reply->reason)); } else { ff_rtsp_parse_line(reply, p, &rt->auth_state); av_strlcat(rt->last_reply, p, sizeof(rt->last_reply)); @@ -1096,6 +987,14 @@ retry: rt->auth_state.auth_type != HTTP_AUTH_NONE) goto retry; + if (reply->status_code > 400){ + av_log(s, AV_LOG_ERROR, "method %s failed: %d%s\n", + method, + reply->status_code, + reply->reason); + av_log(s, AV_LOG_DEBUG, "%s\n", rt->last_reply); + } + return 0; } @@ -1256,7 +1155,7 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, rt->transport = reply->transports[0].transport; } - /* close RTP connection if not choosen */ + /* close RTP connection if not chosen */ if (reply->transports[0].lower_transport != RTSP_LOWER_TRANSPORT_UDP && (lower_transport == RTSP_LOWER_TRANSPORT_UDP)) { url_close(rtsp_st->rtp_handle); @@ -1272,9 +1171,15 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, case RTSP_LOWER_TRANSPORT_UDP: { char url[1024]; - /* XXX: also use address if specified */ - ff_url_join(url, sizeof(url), "rtp", NULL, host, - reply->transports[0].server_port_min, NULL); + /* Use source address if specified */ + if (reply->transports[0].source[0]) { + ff_url_join(url, sizeof(url), "rtp", NULL, + reply->transports[0].source, + reply->transports[0].server_port_min, NULL); + } else { + ff_url_join(url, sizeof(url), "rtp", NULL, host, + reply->transports[0].server_port_min, NULL); + } if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) && rtp_set_remote_url(rtsp_st->rtp_handle, url) < 0) { err = AVERROR_INVALIDDATA; @@ -1289,20 +1194,22 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, break; } case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: { - char url[1024]; - struct in_addr in; + char url[1024], namebuf[50]; + struct sockaddr_storage addr; int port, ttl; - if (reply->transports[0].destination) { - in.s_addr = htonl(reply->transports[0].destination); + if (reply->transports[0].destination.ss_family) { + addr = reply->transports[0].destination; port = reply->transports[0].port_min; ttl = reply->transports[0].ttl; } else { - in = rtsp_st->sdp_ip; + addr = rtsp_st->sdp_ip; port = rtsp_st->sdp_port; ttl = rtsp_st->sdp_ttl; } - ff_url_join(url, sizeof(url), "rtp", NULL, inet_ntoa(in), + getnameinfo((struct sockaddr*) &addr, sizeof(addr), + namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); + ff_url_join(url, sizeof(url), "rtp", NULL, namebuf, port, "?ttl=%d", ttl); if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { err = AVERROR_INVALIDDATA; @@ -1342,6 +1249,7 @@ static int rtsp_read_play(AVFormatContext *s) char cmd[1024]; av_log(s, AV_LOG_DEBUG, "hello state=%d\n", rt->state); + rt->nb_byes = 0; if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { if (rt->state == RTSP_STATE_PAUSED) { @@ -1355,8 +1263,7 @@ static int rtsp_read_play(AVFormatContext *s) if (reply->status_code != RTSP_STATUS_OK) { return -1; } - if (reply->range_start != AV_NOPTS_VALUE && - rt->transport == RTSP_TRANSPORT_RTP) { + if (rt->transport == RTSP_TRANSPORT_RTP) { for (i = 0; i < rt->nb_rtsp_streams; i++) { RTSPStream *rtsp_st = rt->rtsp_streams[i]; RTPDemuxContext *rtpctx = rtsp_st->transport_priv; @@ -1365,12 +1272,15 @@ static int rtsp_read_play(AVFormatContext *s) continue; if (rtsp_st->stream_index >= 0) st = s->streams[rtsp_st->stream_index]; - rtpctx->last_rtcp_ntp_time = AV_NOPTS_VALUE; - rtpctx->first_rtcp_ntp_time = AV_NOPTS_VALUE; - if (st) - rtpctx->range_start_offset = av_rescale_q(reply->range_start, - AV_TIME_BASE_Q, - st->time_base); + ff_rtp_reset_packet_queue(rtpctx); + if (reply->range_start != AV_NOPTS_VALUE) { + rtpctx->last_rtcp_ntp_time = AV_NOPTS_VALUE; + rtpctx->first_rtcp_ntp_time = AV_NOPTS_VALUE; + if (st) + rtpctx->range_start_offset = + av_rescale_q(reply->range_start, AV_TIME_BASE_Q, + st->time_base); + } } } } @@ -1405,6 +1315,7 @@ static int rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply return AVERROR_INVALIDDATA; } + av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", content); /* now we got the SDP description, we parse it */ ret = sdp_parse(s, (const char *)content); av_freep(&content); @@ -1425,7 +1336,7 @@ static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr) rt->start_time = av_gettime(); /* Announce the stream */ - sdp = av_mallocz(8192); + sdp = av_mallocz(SDP_MAX_SIZE); if (sdp == NULL) return AVERROR(ENOMEM); /* We create the SDP based on the RTSP AVFormatContext where we @@ -1444,11 +1355,11 @@ static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr) ff_url_join(sdp_ctx.filename, sizeof(sdp_ctx.filename), "rtsp", NULL, addr, -1, NULL); ctx_array[0] = &sdp_ctx; - if (avf_sdp_create(ctx_array, 1, sdp, 8192)) { + if (avf_sdp_create(ctx_array, 1, sdp, SDP_MAX_SIZE)) { av_free(sdp); return AVERROR_INVALIDDATA; } - av_log(s, AV_LOG_INFO, "SDP:\n%s\n", sdp); + av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", sdp); ff_rtsp_send_cmd_with_content(s, "ANNOUNCE", rt->control_uri, "Content-Type: application/sdp\r\n", reply, NULL, sdp, strlen(sdp)); @@ -1483,6 +1394,7 @@ void ff_rtsp_close_connections(AVFormatContext *s) RTSPState *rt = s->priv_data; if (rt->rtsp_hd_out != rt->rtsp_hd) url_close(rt->rtsp_hd_out); url_close(rt->rtsp_hd); + rt->rtsp_hd = rt->rtsp_hd_out = NULL; } int ff_rtsp_connect(AVFormatContext *s) @@ -1490,9 +1402,8 @@ int ff_rtsp_connect(AVFormatContext *s) RTSPState *rt = s->priv_data; char host[1024], path[1024], tcpname[1024], cmd[2048], auth[128]; char *option_list, *option, *filename; - URLContext *rtsp_hd, *rtsp_hd_out; int port, err, tcp_fd; - RTSPMessageHeader reply1 = {}, *reply = &reply1; + RTSPMessageHeader reply1 = {0}, *reply = &reply1; int lower_transport_mask = 0; char real_challenge[64]; struct sockaddr_storage peer; @@ -1503,7 +1414,7 @@ int ff_rtsp_connect(AVFormatContext *s) redirect: rt->control_transport = RTSP_MODE_PLAIN; /* extract hostname and port */ - ff_url_split(NULL, 0, auth, sizeof(auth), + av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host), &port, path, sizeof(path), s->filename); if (*auth) { av_strlcpy(rt->auth, auth, sizeof(rt->auth)); @@ -1573,12 +1484,12 @@ redirect: char sessioncookie[17]; char headers[1024]; - ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, "%s", path); + ff_url_join(httpname, sizeof(httpname), "http", auth, host, port, "%s", path); snprintf(sessioncookie, sizeof(sessioncookie), "%08x%08x", av_get_random_seed(), av_get_random_seed()); /* GET requests */ - if (url_open(&rtsp_hd, httpname, URL_RDONLY) < 0) { + if (url_alloc(&rt->rtsp_hd, httpname, URL_RDONLY) < 0) { err = AVERROR(EIO); goto fail; } @@ -1590,18 +1501,16 @@ redirect: "Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n", sessioncookie); - ff_http_set_headers(rtsp_hd, headers); + ff_http_set_headers(rt->rtsp_hd, headers); /* complete the connection */ - if (url_read(rtsp_hd, NULL, 0)) { - url_close(rtsp_hd); + if (url_connect(rt->rtsp_hd)) { err = AVERROR(EIO); goto fail; } /* POST requests */ - if (url_open(&rtsp_hd_out, httpname, URL_WRONLY) < 0 ) { - url_close(rtsp_hd); + if (url_alloc(&rt->rtsp_hd_out, httpname, URL_WRONLY) < 0 ) { err = AVERROR(EIO); goto fail; } @@ -1615,23 +1524,44 @@ redirect: "Content-Length: 32767\r\n" "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n", sessioncookie); - ff_http_set_headers(rtsp_hd_out, headers); - ff_http_set_chunked_transfer_encoding(rtsp_hd_out, 0); + ff_http_set_headers(rt->rtsp_hd_out, headers); + ff_http_set_chunked_transfer_encoding(rt->rtsp_hd_out, 0); + + /* Initialize the authentication state for the POST session. The HTTP + * protocol implementation doesn't properly handle multi-pass + * authentication for POST requests, since it would require one of + * the following: + * - implementing Expect: 100-continue, which many HTTP servers + * don't support anyway, even less the RTSP servers that do HTTP + * tunneling + * - sending the whole POST data until getting a 401 reply specifying + * what authentication method to use, then resending all that data + * - waiting for potential 401 replies directly after sending the + * POST header (waiting for some unspecified time) + * Therefore, we copy the full auth state, which works for both basic + * and digest. (For digest, we would have to synchronize the nonce + * count variable between the two sessions, if we'd do more requests + * with the original session, though.) + */ + ff_http_init_auth_state(rt->rtsp_hd_out, rt->rtsp_hd); + /* complete the connection */ + if (url_connect(rt->rtsp_hd_out)) { + err = AVERROR(EIO); + goto fail; + } } else { /* open the tcp connection */ ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL); - if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) { + if (url_open(&rt->rtsp_hd, tcpname, URL_RDWR) < 0) { err = AVERROR(EIO); goto fail; } - rtsp_hd_out = rtsp_hd; + rt->rtsp_hd_out = rt->rtsp_hd; } - rt->rtsp_hd = rtsp_hd; - rt->rtsp_hd_out = rtsp_hd_out; rt->seq = 0; - tcp_fd = url_get_file_handle(rtsp_hd); + tcp_fd = url_get_file_handle(rt->rtsp_hd); if (!getpeername(tcp_fd, (struct sockaddr*) &peer, &peer_len)) { getnameinfo((struct sockaddr*) &peer, peer_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST); @@ -1713,43 +1643,22 @@ redirect: ff_network_close(); return err; } -#endif - -#if CONFIG_RTSP_DEMUXER -static int rtsp_read_header(AVFormatContext *s, - AVFormatParameters *ap) -{ - int ret; - - ret = ff_rtsp_connect(s); - if (ret) - return ret; - - if (ap->initial_pause) { - /* do not start immediately */ - } else { - if (rtsp_read_play(s) < 0) { - ff_rtsp_close_streams(s); - ff_rtsp_close_connections(s); - return AVERROR_INVALIDDATA; - } - } - - return 0; -} +#endif /* CONFIG_RTSP_DEMUXER || CONFIG_RTSP_MUXER */ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, - uint8_t *buf, int buf_size) + uint8_t *buf, int buf_size, int64_t wait_end) { RTSPState *rt = s->priv_data; RTSPStream *rtsp_st; fd_set rfds; - int fd, fd_max, n, i, ret, tcp_fd, timeout_cnt = 0; + int fd, fd_rtcp, fd_max, n, i, ret, tcp_fd, timeout_cnt = 0; struct timeval tv; for (;;) { if (url_interrupt_cb()) return AVERROR(EINTR); + if (wait_end && wait_end - av_gettime() < 0) + return AVERROR(EAGAIN); FD_ZERO(&rfds); if (rt->rtsp_hd) { tcp_fd = fd_max = url_get_file_handle(rt->rtsp_hd); @@ -1761,12 +1670,12 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, for (i = 0; i < rt->nb_rtsp_streams; i++) { rtsp_st = rt->rtsp_streams[i]; if (rtsp_st->rtp_handle) { - /* currently, we cannot probe RTCP handle because of - * blocking restrictions */ fd = url_get_file_handle(rtsp_st->rtp_handle); - if (fd > fd_max) - fd_max = fd; + fd_rtcp = rtp_get_rtcp_file_handle(rtsp_st->rtp_handle); + if (FFMAX(fd, fd_rtcp) > fd_max) + fd_max = FFMAX(fd, fd_rtcp); FD_SET(fd, &rfds); + FD_SET(fd_rtcp, &rfds); } } tv.tv_sec = 0; @@ -1778,7 +1687,8 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, rtsp_st = rt->rtsp_streams[i]; if (rtsp_st->rtp_handle) { fd = url_get_file_handle(rtsp_st->rtp_handle); - if (FD_ISSET(fd, &rfds)) { + fd_rtcp = rtp_get_rtcp_file_handle(rtsp_st->rtp_handle); + if (FD_ISSET(fd_rtcp, &rfds) || FD_ISSET(fd, &rfds)) { ret = url_read(rtsp_st->rtp_handle, buf, buf_size); if (ret > 0) { *prtsp_st = rtsp_st; @@ -1807,65 +1717,17 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, } static int tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, - uint8_t *buf, int buf_size) -{ - RTSPState *rt = s->priv_data; - int id, len, i, ret; - RTSPStream *rtsp_st; - -#ifdef DEBUG_RTP_TCP - dprintf(s, "tcp_read_packet:\n"); -#endif -redo: - for (;;) { - RTSPMessageHeader reply; - - ret = ff_rtsp_read_reply(s, &reply, NULL, 1); - if (ret == -1) - return -1; - if (ret == 1) /* received '$' */ - break; - /* XXX: parse message */ - if (rt->state != RTSP_STATE_STREAMING) - return 0; - } - ret = url_read_complete(rt->rtsp_hd, buf, 3); - if (ret != 3) - return -1; - id = buf[0]; - len = AV_RB16(buf + 1); -#ifdef DEBUG_RTP_TCP - dprintf(s, "id=%d len=%d\n", id, len); -#endif - if (len > buf_size || len < 12) - goto redo; - /* get the data */ - ret = url_read_complete(rt->rtsp_hd, buf, len); - if (ret != len) - return -1; - if (rt->transport == RTSP_TRANSPORT_RDT && - ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL) < 0) - return -1; - - /* find the matching stream */ - for (i = 0; i < rt->nb_rtsp_streams; i++) { - rtsp_st = rt->rtsp_streams[i]; - if (id >= rtsp_st->interleaved_min && - id <= rtsp_st->interleaved_max) - goto found; - } - goto redo; -found: - *prtsp_st = rtsp_st; - return len; -} + uint8_t *buf, int buf_size); static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) { RTSPState *rt = s->priv_data; int ret, len; - uint8_t buf[10 * RTP_MAX_PACKET_LENGTH]; - RTSPStream *rtsp_st; + RTSPStream *rtsp_st, *first_queue_st = NULL; + int64_t wait_end = 0; + + if (rt->nb_byes == rt->nb_rtsp_streams) + return AVERROR_EOF; /* get next frames from the same RTP packet */ if (rt->cur_transport_priv) { @@ -1882,30 +1744,58 @@ static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) rt->cur_transport_priv = NULL; } + if (rt->transport == RTSP_TRANSPORT_RTP) { + int i; + int64_t first_queue_time = 0; + for (i = 0; i < rt->nb_rtsp_streams; i++) { + RTPDemuxContext *rtpctx = rt->rtsp_streams[i]->transport_priv; + int64_t queue_time = ff_rtp_queued_packet_time(rtpctx); + if (queue_time && (queue_time - first_queue_time < 0 || + !first_queue_time)) { + first_queue_time = queue_time; + first_queue_st = rt->rtsp_streams[i]; + } + } + if (first_queue_time) + wait_end = first_queue_time + s->max_delay; + } + /* read next RTP packet */ redo: + if (!rt->recvbuf) { + rt->recvbuf = av_malloc(RECVBUF_SIZE); + if (!rt->recvbuf) + return AVERROR(ENOMEM); + } + switch(rt->lower_transport) { default: #if CONFIG_RTSP_DEMUXER case RTSP_LOWER_TRANSPORT_TCP: - len = tcp_read_packet(s, &rtsp_st, buf, sizeof(buf)); + len = tcp_read_packet(s, &rtsp_st, rt->recvbuf, RECVBUF_SIZE); break; #endif case RTSP_LOWER_TRANSPORT_UDP: case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: - len = udp_read_packet(s, &rtsp_st, buf, sizeof(buf)); + len = udp_read_packet(s, &rtsp_st, rt->recvbuf, RECVBUF_SIZE, wait_end); if (len >=0 && rtsp_st->transport_priv && rt->transport == RTSP_TRANSPORT_RTP) rtp_check_and_send_back_rr(rtsp_st->transport_priv, len); break; } + if (len == AVERROR(EAGAIN) && first_queue_st && + rt->transport == RTSP_TRANSPORT_RTP) { + rtsp_st = first_queue_st; + ret = rtp_parse_packet(rtsp_st->transport_priv, pkt, NULL, 0); + goto end; + } if (len < 0) return len; if (len == 0) return AVERROR_EOF; if (rt->transport == RTSP_TRANSPORT_RDT) { - ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, buf, len); + ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); } else { - ret = rtp_parse_packet(rtsp_st->transport_priv, pkt, buf, len); + ret = rtp_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); if (ret < 0) { /* Either bad packet, or a RTCP packet. Check if the * first_rtcp_ntp_time field was initialized. */ @@ -1917,14 +1807,24 @@ static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) * as this one. */ int i; for (i = 0; i < rt->nb_rtsp_streams; i++) { - RTPDemuxContext *rtpctx2 = rtsp_st->transport_priv; + RTPDemuxContext *rtpctx2 = rt->rtsp_streams[i]->transport_priv; if (rtpctx2 && rtpctx2->first_rtcp_ntp_time == AV_NOPTS_VALUE) rtpctx2->first_rtcp_ntp_time = rtpctx->first_rtcp_ntp_time; } } + if (ret == -RTCP_BYE) { + rt->nb_byes++; + + av_log(s, AV_LOG_DEBUG, "Received BYE for stream %d (%d/%d)\n", + rtsp_st->stream_index, rt->nb_byes, rt->nb_rtsp_streams); + + if (rt->nb_byes == rt->nb_rtsp_streams) + return AVERROR_EOF; + } } } +end: if (ret < 0) goto redo; if (ret == 1) @@ -1934,6 +1834,88 @@ static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) return ret; } +#if CONFIG_RTSP_DEMUXER +static int rtsp_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + RTSPState *rt = s->priv_data; + int ret; + + ret = ff_rtsp_connect(s); + if (ret) + return ret; + + rt->real_setup_cache = av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache)); + if (!rt->real_setup_cache) + return AVERROR(ENOMEM); + rt->real_setup = rt->real_setup_cache + s->nb_streams * sizeof(*rt->real_setup); + + if (ap->initial_pause) { + /* do not start immediately */ + } else { + if (rtsp_read_play(s) < 0) { + ff_rtsp_close_streams(s); + ff_rtsp_close_connections(s); + return AVERROR_INVALIDDATA; + } + } + + return 0; +} + +static int tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, + uint8_t *buf, int buf_size) +{ + RTSPState *rt = s->priv_data; + int id, len, i, ret; + RTSPStream *rtsp_st; + +#ifdef DEBUG_RTP_TCP + dprintf(s, "tcp_read_packet:\n"); +#endif +redo: + for (;;) { + RTSPMessageHeader reply; + + ret = ff_rtsp_read_reply(s, &reply, NULL, 1); + if (ret < 0) + return ret; + if (ret == 1) /* received '$' */ + break; + /* XXX: parse message */ + if (rt->state != RTSP_STATE_STREAMING) + return 0; + } + ret = url_read_complete(rt->rtsp_hd, buf, 3); + if (ret != 3) + return -1; + id = buf[0]; + len = AV_RB16(buf + 1); +#ifdef DEBUG_RTP_TCP + dprintf(s, "id=%d len=%d\n", id, len); +#endif + if (len > buf_size || len < 12) + goto redo; + /* get the data */ + ret = url_read_complete(rt->rtsp_hd, buf, len); + if (ret != len) + return -1; + if (rt->transport == RTSP_TRANSPORT_RDT && + ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL) < 0) + return -1; + + /* find the matching stream */ + for (i = 0; i < rt->nb_rtsp_streams; i++) { + rtsp_st = rt->rtsp_streams[i]; + if (id >= rtsp_st->interleaved_min && + id <= rtsp_st->interleaved_max) + goto found; + } + goto redo; +found: + *prtsp_st = rtsp_st; + return len; +} static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) { RTSPState *rt = s->priv_data; @@ -1943,13 +1925,12 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) if (rt->server_type == RTSP_SERVER_REAL) { int i; - enum AVDiscard cache[MAX_STREAMS]; for (i = 0; i < s->nb_streams; i++) - cache[i] = s->streams[i]->discard; + rt->real_setup[i] = s->streams[i]->discard; if (!rt->need_subscription) { - if (memcmp (cache, rt->real_setup_cache, + if (memcmp (rt->real_setup, rt->real_setup_cache, sizeof(enum AVDiscard) * s->nb_streams)) { snprintf(cmd, sizeof(cmd), "Unsubscribe: %s\r\n", @@ -1965,7 +1946,7 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) if (rt->need_subscription) { int r, rule_nr, first = 1; - memcpy(rt->real_setup_cache, cache, + memcpy(rt->real_setup_cache, rt->real_setup, sizeof(enum AVDiscard) * s->nb_streams); rt->last_subscription[0] = 0; @@ -2005,9 +1986,7 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; /* send dummy request to keep TCP connection alive */ - if ((rt->server_type == RTSP_SERVER_WMS || - rt->server_type == RTSP_SERVER_REAL) && - (av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2) { + if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2) { if (rt->server_type == RTSP_SERVER_WMS) { ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL); } else { @@ -2077,6 +2056,8 @@ static int rtsp_read_close(AVFormatContext *s) ff_rtsp_close_streams(s); ff_rtsp_close_connections(s); ff_network_close(); + rt->real_setup = NULL; + av_freep(&rt->real_setup_cache); return 0; } @@ -2093,16 +2074,16 @@ AVInputFormat rtsp_demuxer = { .read_play = rtsp_read_play, .read_pause = rtsp_read_pause, }; -#endif +#endif /* CONFIG_RTSP_DEMUXER */ static int sdp_probe(AVProbeData *p1) { const char *p = p1->buf, *p_end = p1->buf + p1->buf_size; - /* we look for a line beginning "c=IN IP4" */ + /* we look for a line beginning "c=IN IP" */ while (p < p_end && *p != '\0') { - if (p + sizeof("c=IN IP4") - 1 < p_end && - av_strstart(p, "c=IN IP4", NULL)) + if (p + sizeof("c=IN IP") - 1 < p_end && + av_strstart(p, "c=IN IP", NULL)) return AVPROBE_SCORE_MAX / 2; while (p < p_end - 1 && *p != '\n') p++; @@ -2114,8 +2095,6 @@ static int sdp_probe(AVProbeData *p1) return 0; } -#define SDP_MAX_SIZE 8192 - static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) { RTSPState *rt = s->priv_data; @@ -2142,10 +2121,13 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) /* open each RTP stream */ for (i = 0; i < rt->nb_rtsp_streams; i++) { + char namebuf[50]; rtsp_st = rt->rtsp_streams[i]; + getnameinfo((struct sockaddr*) &rtsp_st->sdp_ip, sizeof(rtsp_st->sdp_ip), + namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); ff_url_join(url, sizeof(url), "rtp", NULL, - inet_ntoa(rtsp_st->sdp_ip), rtsp_st->sdp_port, + namebuf, rtsp_st->sdp_port, "?localport=%d&ttl=%d", rtsp_st->sdp_port, rtsp_st->sdp_ttl); if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) {