yading@11: /* yading@11: * RTSP demuxer yading@11: * Copyright (c) 2002 Fabrice Bellard yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include "libavutil/avstring.h" yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "libavutil/mathematics.h" yading@11: #include "libavutil/random_seed.h" yading@11: #include "libavutil/time.h" yading@11: #include "avformat.h" yading@11: yading@11: #include "internal.h" yading@11: #include "network.h" yading@11: #include "os_support.h" yading@11: #include "rtsp.h" yading@11: #include "rdt.h" yading@11: #include "url.h" yading@11: yading@11: static const struct RTSPStatusMessage { yading@11: enum RTSPStatusCode code; yading@11: const char *message; yading@11: } status_messages[] = { yading@11: { RTSP_STATUS_OK, "OK" }, yading@11: { RTSP_STATUS_METHOD, "Method Not Allowed" }, yading@11: { RTSP_STATUS_BANDWIDTH, "Not Enough Bandwidth" }, yading@11: { RTSP_STATUS_SESSION, "Session Not Found" }, yading@11: { RTSP_STATUS_STATE, "Method Not Valid in This State" }, yading@11: { RTSP_STATUS_AGGREGATE, "Aggregate operation not allowed" }, yading@11: { RTSP_STATUS_ONLY_AGGREGATE, "Only aggregate operation allowed" }, yading@11: { RTSP_STATUS_TRANSPORT, "Unsupported transport" }, yading@11: { RTSP_STATUS_INTERNAL, "Internal Server Error" }, yading@11: { RTSP_STATUS_SERVICE, "Service Unavailable" }, yading@11: { RTSP_STATUS_VERSION, "RTSP Version not supported" }, yading@11: { 0, "NULL" } yading@11: }; yading@11: yading@11: static int rtsp_read_close(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: yading@11: if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) yading@11: ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); yading@11: yading@11: ff_rtsp_close_streams(s); yading@11: ff_rtsp_close_connections(s); yading@11: ff_network_close(); yading@11: rt->real_setup = NULL; yading@11: av_freep(&rt->real_setup_cache); yading@11: return 0; yading@11: } yading@11: yading@11: static inline int read_line(AVFormatContext *s, char *rbuf, const int rbufsize, yading@11: int *rbuflen) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: int idx = 0; yading@11: int ret = 0; yading@11: *rbuflen = 0; yading@11: yading@11: do { yading@11: ret = ffurl_read_complete(rt->rtsp_hd, rbuf + idx, 1); yading@11: if (ret <= 0) yading@11: return ret ? ret : AVERROR_EOF; yading@11: if (rbuf[idx] == '\r') { yading@11: /* Ignore */ yading@11: } else if (rbuf[idx] == '\n') { yading@11: rbuf[idx] = '\0'; yading@11: *rbuflen = idx; yading@11: return 0; yading@11: } else yading@11: idx++; yading@11: } while (idx < rbufsize); yading@11: av_log(s, AV_LOG_ERROR, "Message too long\n"); yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: static int rtsp_send_reply(AVFormatContext *s, enum RTSPStatusCode code, yading@11: const char *extracontent, uint16_t seq) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: char message[4096]; yading@11: int index = 0; yading@11: while (status_messages[index].code) { yading@11: if (status_messages[index].code == code) { yading@11: snprintf(message, sizeof(message), "RTSP/1.0 %d %s\r\n", yading@11: code, status_messages[index].message); yading@11: break; yading@11: } yading@11: index++; yading@11: } yading@11: if (!status_messages[index].code) yading@11: return AVERROR(EINVAL); yading@11: av_strlcatf(message, sizeof(message), "CSeq: %d\r\n", seq); yading@11: av_strlcatf(message, sizeof(message), "Server: %s\r\n", LIBAVFORMAT_IDENT); yading@11: if (extracontent) yading@11: av_strlcat(message, extracontent, sizeof(message)); yading@11: av_strlcat(message, "\r\n", sizeof(message)); yading@11: av_dlog(s, "Sending response:\n%s", message); yading@11: ffurl_write(rt->rtsp_hd, message, strlen(message)); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static inline int check_sessionid(AVFormatContext *s, yading@11: RTSPMessageHeader *request) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: unsigned char *session_id = rt->session_id; yading@11: if (!session_id[0]) { yading@11: av_log(s, AV_LOG_WARNING, "There is no session-id at the moment\n"); yading@11: return 0; yading@11: } yading@11: if (strcmp(session_id, request->session_id)) { yading@11: av_log(s, AV_LOG_ERROR, "Unexpected session-id %s\n", yading@11: request->session_id); yading@11: rtsp_send_reply(s, RTSP_STATUS_SESSION, NULL, request->seq); yading@11: return AVERROR_STREAM_NOT_FOUND; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: static inline int rtsp_read_request(AVFormatContext *s, yading@11: RTSPMessageHeader *request, yading@11: const char *method) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: char rbuf[1024]; yading@11: int rbuflen, ret; yading@11: do { yading@11: ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); yading@11: if (ret) yading@11: return ret; yading@11: if (rbuflen > 1) { yading@11: av_dlog(s, "Parsing[%d]: %s\n", rbuflen, rbuf); yading@11: ff_rtsp_parse_line(request, rbuf, rt, method); yading@11: } yading@11: } while (rbuflen > 0); yading@11: if (request->seq != rt->seq + 1) { yading@11: av_log(s, AV_LOG_ERROR, "Unexpected Sequence number %d\n", yading@11: request->seq); yading@11: return AVERROR(EINVAL); yading@11: } yading@11: if (rt->session_id[0] && strcmp(method, "OPTIONS")) { yading@11: ret = check_sessionid(s, request); yading@11: if (ret) yading@11: return ret; yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int rtsp_read_announce(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: RTSPMessageHeader request = { 0 }; yading@11: char sdp[4096]; yading@11: int ret; yading@11: yading@11: ret = rtsp_read_request(s, &request, "ANNOUNCE"); yading@11: if (ret) yading@11: return ret; yading@11: rt->seq++; yading@11: if (strcmp(request.content_type, "application/sdp")) { yading@11: av_log(s, AV_LOG_ERROR, "Unexpected content type %s\n", yading@11: request.content_type); yading@11: rtsp_send_reply(s, RTSP_STATUS_SERVICE, NULL, request.seq); yading@11: return AVERROR_OPTION_NOT_FOUND; yading@11: } yading@11: if (request.content_length && request.content_length < sizeof(sdp) - 1) { yading@11: /* Read SDP */ yading@11: if (ffurl_read_complete(rt->rtsp_hd, sdp, request.content_length) yading@11: < request.content_length) { yading@11: av_log(s, AV_LOG_ERROR, yading@11: "Unable to get complete SDP Description in ANNOUNCE\n"); yading@11: rtsp_send_reply(s, RTSP_STATUS_INTERNAL, NULL, request.seq); yading@11: return AVERROR(EIO); yading@11: } yading@11: sdp[request.content_length] = '\0'; yading@11: av_log(s, AV_LOG_VERBOSE, "SDP: %s\n", sdp); yading@11: ret = ff_sdp_parse(s, sdp); yading@11: if (ret) yading@11: return ret; yading@11: rtsp_send_reply(s, RTSP_STATUS_OK, NULL, request.seq); yading@11: return 0; yading@11: } yading@11: av_log(s, AV_LOG_ERROR, yading@11: "Content-Length header value exceeds sdp allocated buffer (4KB)\n"); yading@11: rtsp_send_reply(s, RTSP_STATUS_INTERNAL, yading@11: "Content-Length exceeds buffer size", request.seq); yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: static int rtsp_read_options(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: RTSPMessageHeader request = { 0 }; yading@11: int ret = 0; yading@11: yading@11: /* Parsing headers */ yading@11: ret = rtsp_read_request(s, &request, "OPTIONS"); yading@11: if (ret) yading@11: return ret; yading@11: rt->seq++; yading@11: /* Send Reply */ yading@11: rtsp_send_reply(s, RTSP_STATUS_OK, yading@11: "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, RECORD\r\n", yading@11: request.seq); yading@11: return 0; yading@11: } yading@11: yading@11: static int rtsp_read_setup(AVFormatContext *s, char* host, char *controlurl) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: RTSPMessageHeader request = { 0 }; yading@11: int ret = 0; yading@11: char url[1024]; yading@11: RTSPStream *rtsp_st; yading@11: char responseheaders[1024]; yading@11: int localport = -1; yading@11: int transportidx = 0; yading@11: int streamid = 0; yading@11: yading@11: ret = rtsp_read_request(s, &request, "SETUP"); yading@11: if (ret) yading@11: return ret; yading@11: rt->seq++; yading@11: if (!request.nb_transports) { yading@11: av_log(s, AV_LOG_ERROR, "No transport defined in SETUP\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: for (transportidx = 0; transportidx < request.nb_transports; yading@11: transportidx++) { yading@11: if (!request.transports[transportidx].mode_record || yading@11: (request.transports[transportidx].lower_transport != yading@11: RTSP_LOWER_TRANSPORT_UDP && yading@11: request.transports[transportidx].lower_transport != yading@11: RTSP_LOWER_TRANSPORT_TCP)) { yading@11: av_log(s, AV_LOG_ERROR, "mode=record/receive not set or transport" yading@11: " protocol not supported (yet)\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: } yading@11: if (request.nb_transports > 1) yading@11: av_log(s, AV_LOG_WARNING, "More than one transport not supported, " yading@11: "using first of all\n"); yading@11: for (streamid = 0; streamid < rt->nb_rtsp_streams; streamid++) { yading@11: if (!strcmp(rt->rtsp_streams[streamid]->control_url, yading@11: controlurl)) yading@11: break; yading@11: } yading@11: if (streamid == rt->nb_rtsp_streams) { yading@11: av_log(s, AV_LOG_ERROR, "Unable to find requested track\n"); yading@11: return AVERROR_STREAM_NOT_FOUND; yading@11: } yading@11: rtsp_st = rt->rtsp_streams[streamid]; yading@11: localport = rt->rtp_port_min; yading@11: yading@11: if (request.transports[0].lower_transport == RTSP_LOWER_TRANSPORT_TCP) { yading@11: rt->lower_transport = RTSP_LOWER_TRANSPORT_TCP; yading@11: if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) { yading@11: rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); yading@11: return ret; yading@11: } yading@11: rtsp_st->interleaved_min = request.transports[0].interleaved_min; yading@11: rtsp_st->interleaved_max = request.transports[0].interleaved_max; yading@11: snprintf(responseheaders, sizeof(responseheaders), "Transport: " yading@11: "RTP/AVP/TCP;unicast;mode=receive;interleaved=%d-%d" yading@11: "\r\n", request.transports[0].interleaved_min, yading@11: request.transports[0].interleaved_max); yading@11: } else { yading@11: do { yading@11: ff_url_join(url, sizeof(url), "rtp", NULL, host, localport, NULL); yading@11: av_dlog(s, "Opening: %s", url); yading@11: ret = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, yading@11: &s->interrupt_callback, NULL); yading@11: if (ret) yading@11: localport += 2; yading@11: } while (ret || localport > rt->rtp_port_max); yading@11: if (localport > rt->rtp_port_max) { yading@11: rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); yading@11: return ret; yading@11: } yading@11: yading@11: av_dlog(s, "Listening on: %d", yading@11: ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle)); yading@11: if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) { yading@11: rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); yading@11: return ret; yading@11: } yading@11: yading@11: localport = ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle); yading@11: snprintf(responseheaders, sizeof(responseheaders), "Transport: " yading@11: "RTP/AVP/UDP;unicast;mode=receive;source=%s;" yading@11: "client_port=%d-%d;server_port=%d-%d\r\n", yading@11: host, request.transports[0].client_port_min, yading@11: request.transports[0].client_port_max, localport, yading@11: localport + 1); yading@11: } yading@11: yading@11: /* Establish sessionid if not previously set */ yading@11: /* Put this in a function? */ yading@11: /* RFC 2326: session id must be at least 8 digits */ yading@11: while (strlen(rt->session_id) < 8) yading@11: av_strlcatf(rt->session_id, 512, "%u", av_get_random_seed()); yading@11: yading@11: av_strlcatf(responseheaders, sizeof(responseheaders), "Session: %s\r\n", yading@11: rt->session_id); yading@11: /* Send Reply */ yading@11: rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq); yading@11: yading@11: rt->state = RTSP_STATE_PAUSED; yading@11: return 0; yading@11: } yading@11: yading@11: static int rtsp_read_record(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: RTSPMessageHeader request = { 0 }; yading@11: int ret = 0; yading@11: char responseheaders[1024]; yading@11: yading@11: ret = rtsp_read_request(s, &request, "RECORD"); yading@11: if (ret) yading@11: return ret; yading@11: ret = check_sessionid(s, &request); yading@11: if (ret) yading@11: return ret; yading@11: rt->seq++; yading@11: snprintf(responseheaders, sizeof(responseheaders), "Session: %s\r\n", yading@11: rt->session_id); yading@11: rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq); yading@11: yading@11: rt->state = RTSP_STATE_STREAMING; yading@11: return 0; yading@11: } yading@11: yading@11: static inline int parse_command_line(AVFormatContext *s, const char *line, yading@11: int linelen, char *uri, int urisize, yading@11: char *method, int methodsize, yading@11: enum RTSPMethod *methodcode) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: const char *linept, *searchlinept; yading@11: linept = strchr(line, ' '); yading@11: if (linept - line > methodsize - 1) { yading@11: av_log(s, AV_LOG_ERROR, "Method string too long\n"); yading@11: return AVERROR(EIO); yading@11: } yading@11: memcpy(method, line, linept - line); yading@11: method[linept - line] = '\0'; yading@11: linept++; yading@11: if (!strcmp(method, "ANNOUNCE")) yading@11: *methodcode = ANNOUNCE; yading@11: else if (!strcmp(method, "OPTIONS")) yading@11: *methodcode = OPTIONS; yading@11: else if (!strcmp(method, "RECORD")) yading@11: *methodcode = RECORD; yading@11: else if (!strcmp(method, "SETUP")) yading@11: *methodcode = SETUP; yading@11: else if (!strcmp(method, "PAUSE")) yading@11: *methodcode = PAUSE; yading@11: else if (!strcmp(method, "TEARDOWN")) yading@11: *methodcode = TEARDOWN; yading@11: else yading@11: *methodcode = UNKNOWN; yading@11: /* Check method with the state */ yading@11: if (rt->state == RTSP_STATE_IDLE) { yading@11: if ((*methodcode != ANNOUNCE) && (*methodcode != OPTIONS)) { yading@11: av_log(s, AV_LOG_ERROR, "Unexpected command in Idle State %s\n", yading@11: line); yading@11: return AVERROR_PROTOCOL_NOT_FOUND; yading@11: } yading@11: } else if (rt->state == RTSP_STATE_PAUSED) { yading@11: if ((*methodcode != OPTIONS) && (*methodcode != RECORD) yading@11: && (*methodcode != SETUP)) { yading@11: av_log(s, AV_LOG_ERROR, "Unexpected command in Paused State %s\n", yading@11: line); yading@11: return AVERROR_PROTOCOL_NOT_FOUND; yading@11: } yading@11: } else if (rt->state == RTSP_STATE_STREAMING) { yading@11: if ((*methodcode != PAUSE) && (*methodcode != OPTIONS) yading@11: && (*methodcode != TEARDOWN)) { yading@11: av_log(s, AV_LOG_ERROR, "Unexpected command in Streaming State" yading@11: " %s\n", line); yading@11: return AVERROR_PROTOCOL_NOT_FOUND; yading@11: } yading@11: } else { yading@11: av_log(s, AV_LOG_ERROR, "Unexpected State [%d]\n", rt->state); yading@11: return AVERROR_BUG; yading@11: } yading@11: yading@11: searchlinept = strchr(linept, ' '); yading@11: if (searchlinept == NULL) { yading@11: av_log(s, AV_LOG_ERROR, "Error parsing message URI\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: if (searchlinept - linept > urisize - 1) { yading@11: av_log(s, AV_LOG_ERROR, "uri string length exceeded buffer size\n"); yading@11: return AVERROR(EIO); yading@11: } yading@11: memcpy(uri, linept, searchlinept - linept); yading@11: uri[searchlinept - linept] = '\0'; yading@11: if (strcmp(rt->control_uri, uri)) { yading@11: char host[128], path[512], auth[128]; yading@11: int port; yading@11: char ctl_host[128], ctl_path[512], ctl_auth[128]; yading@11: int ctl_port; yading@11: av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host), &port, yading@11: path, sizeof(path), uri); yading@11: av_url_split(NULL, 0, ctl_auth, sizeof(ctl_auth), ctl_host, yading@11: sizeof(ctl_host), &ctl_port, ctl_path, sizeof(ctl_path), yading@11: rt->control_uri); yading@11: if (strcmp(host, ctl_host)) yading@11: av_log(s, AV_LOG_INFO, "Host %s differs from expected %s\n", yading@11: host, ctl_host); yading@11: if (strcmp(path, ctl_path) && *methodcode != SETUP) yading@11: av_log(s, AV_LOG_WARNING, "WARNING: Path %s differs from expected" yading@11: " %s\n", path, ctl_path); yading@11: if (*methodcode == ANNOUNCE) { yading@11: av_log(s, AV_LOG_INFO, yading@11: "Updating control URI to %s\n", uri); yading@11: av_strlcpy(rt->control_uri, uri, sizeof(rt->control_uri)); yading@11: } yading@11: } yading@11: yading@11: linept = searchlinept + 1; yading@11: if (!av_strstart(linept, "RTSP/1.0", NULL)) { yading@11: av_log(s, AV_LOG_ERROR, "Error parsing protocol or version\n"); yading@11: return AVERROR_PROTOCOL_NOT_FOUND; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: int ff_rtsp_parse_streaming_commands(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: unsigned char rbuf[4096]; yading@11: unsigned char method[10]; yading@11: char uri[500]; yading@11: int ret; yading@11: int rbuflen = 0; yading@11: RTSPMessageHeader request = { 0 }; yading@11: enum RTSPMethod methodcode; yading@11: yading@11: ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); yading@11: if (ret < 0) yading@11: return ret; yading@11: ret = parse_command_line(s, rbuf, rbuflen, uri, sizeof(uri), method, yading@11: sizeof(method), &methodcode); yading@11: if (ret) { yading@11: av_log(s, AV_LOG_ERROR, "RTSP: Unexpected Command\n"); yading@11: return ret; yading@11: } yading@11: yading@11: ret = rtsp_read_request(s, &request, method); yading@11: if (ret) yading@11: return ret; yading@11: rt->seq++; yading@11: if (methodcode == PAUSE) { yading@11: rt->state = RTSP_STATE_PAUSED; yading@11: ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq); yading@11: // TODO: Missing date header in response yading@11: } else if (methodcode == OPTIONS) { yading@11: ret = rtsp_send_reply(s, RTSP_STATUS_OK, yading@11: "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, " yading@11: "RECORD\r\n", request.seq); yading@11: } else if (methodcode == TEARDOWN) { yading@11: rt->state = RTSP_STATE_IDLE; yading@11: ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq); yading@11: return 0; yading@11: } yading@11: return ret; yading@11: } yading@11: yading@11: static int rtsp_read_play(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: RTSPMessageHeader reply1, *reply = &reply1; yading@11: int i; yading@11: char cmd[1024]; yading@11: yading@11: av_log(s, AV_LOG_DEBUG, "hello state=%d\n", rt->state); yading@11: rt->nb_byes = 0; yading@11: yading@11: if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { yading@11: if (rt->transport == RTSP_TRANSPORT_RTP) { yading@11: for (i = 0; i < rt->nb_rtsp_streams; i++) { yading@11: RTSPStream *rtsp_st = rt->rtsp_streams[i]; yading@11: RTPDemuxContext *rtpctx = rtsp_st->transport_priv; yading@11: if (!rtpctx) yading@11: continue; yading@11: ff_rtp_reset_packet_queue(rtpctx); yading@11: rtpctx->last_rtcp_ntp_time = AV_NOPTS_VALUE; yading@11: rtpctx->first_rtcp_ntp_time = AV_NOPTS_VALUE; yading@11: rtpctx->base_timestamp = 0; yading@11: rtpctx->timestamp = 0; yading@11: rtpctx->unwrapped_timestamp = 0; yading@11: rtpctx->rtcp_ts_offset = 0; yading@11: } yading@11: } yading@11: if (rt->state == RTSP_STATE_PAUSED) { yading@11: cmd[0] = 0; yading@11: } else { yading@11: snprintf(cmd, sizeof(cmd), yading@11: "Range: npt=%"PRId64".%03"PRId64"-\r\n", yading@11: rt->seek_timestamp / AV_TIME_BASE, yading@11: rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); yading@11: } yading@11: ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); yading@11: if (reply->status_code != RTSP_STATUS_OK) { yading@11: return -1; yading@11: } yading@11: if (rt->transport == RTSP_TRANSPORT_RTP && yading@11: reply->range_start != AV_NOPTS_VALUE) { yading@11: for (i = 0; i < rt->nb_rtsp_streams; i++) { yading@11: RTSPStream *rtsp_st = rt->rtsp_streams[i]; yading@11: RTPDemuxContext *rtpctx = rtsp_st->transport_priv; yading@11: AVStream *st = NULL; yading@11: if (!rtpctx || rtsp_st->stream_index < 0) yading@11: continue; yading@11: st = s->streams[rtsp_st->stream_index]; yading@11: rtpctx->range_start_offset = yading@11: av_rescale_q(reply->range_start, AV_TIME_BASE_Q, yading@11: st->time_base); yading@11: } yading@11: } yading@11: } yading@11: rt->state = RTSP_STATE_STREAMING; yading@11: return 0; yading@11: } yading@11: yading@11: /* pause the stream */ yading@11: static int rtsp_read_pause(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: RTSPMessageHeader reply1, *reply = &reply1; yading@11: yading@11: if (rt->state != RTSP_STATE_STREAMING) yading@11: return 0; yading@11: else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { yading@11: ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, NULL); yading@11: if (reply->status_code != RTSP_STATUS_OK) { yading@11: return -1; yading@11: } yading@11: } yading@11: rt->state = RTSP_STATE_PAUSED; yading@11: return 0; yading@11: } yading@11: yading@11: int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: char cmd[1024]; yading@11: unsigned char *content = NULL; yading@11: int ret; yading@11: yading@11: /* describe the stream */ yading@11: snprintf(cmd, sizeof(cmd), yading@11: "Accept: application/sdp\r\n"); yading@11: if (rt->server_type == RTSP_SERVER_REAL) { yading@11: /** yading@11: * The Require: attribute is needed for proper streaming from yading@11: * Realmedia servers. yading@11: */ yading@11: av_strlcat(cmd, yading@11: "Require: com.real.retain-entity-for-setup\r\n", yading@11: sizeof(cmd)); yading@11: } yading@11: ff_rtsp_send_cmd(s, "DESCRIBE", rt->control_uri, cmd, reply, &content); yading@11: if (!content) yading@11: return AVERROR_INVALIDDATA; yading@11: if (reply->status_code != RTSP_STATUS_OK) { yading@11: av_freep(&content); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", content); yading@11: /* now we got the SDP description, we parse it */ yading@11: ret = ff_sdp_parse(s, (const char *)content); yading@11: av_freep(&content); yading@11: if (ret < 0) yading@11: return ret; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int rtsp_listen(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: char host[128], path[512], auth[128]; yading@11: char uri[500]; yading@11: int port; yading@11: char tcpname[500]; yading@11: unsigned char rbuf[4096]; yading@11: unsigned char method[10]; yading@11: int rbuflen = 0; yading@11: int ret; yading@11: enum RTSPMethod methodcode; yading@11: yading@11: /* extract hostname and port */ yading@11: av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host), &port, yading@11: path, sizeof(path), s->filename); yading@11: yading@11: /* ff_url_join. No authorization by now (NULL) */ yading@11: ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL, host, yading@11: port, "%s", path); yading@11: yading@11: if (port < 0) yading@11: port = RTSP_DEFAULT_PORT; yading@11: yading@11: /* Create TCP connection */ yading@11: ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, yading@11: "?listen&listen_timeout=%d", rt->initial_timeout * 1000); yading@11: yading@11: if (ret = ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE, yading@11: &s->interrupt_callback, NULL)) { yading@11: av_log(s, AV_LOG_ERROR, "Unable to open RTSP for listening\n"); yading@11: return ret; yading@11: } yading@11: rt->state = RTSP_STATE_IDLE; yading@11: rt->rtsp_hd_out = rt->rtsp_hd; yading@11: for (;;) { /* Wait for incoming RTSP messages */ yading@11: ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); yading@11: if (ret < 0) yading@11: return ret; yading@11: ret = parse_command_line(s, rbuf, rbuflen, uri, sizeof(uri), method, yading@11: sizeof(method), &methodcode); yading@11: if (ret) { yading@11: av_log(s, AV_LOG_ERROR, "RTSP: Unexpected Command\n"); yading@11: return ret; yading@11: } yading@11: yading@11: if (methodcode == ANNOUNCE) { yading@11: ret = rtsp_read_announce(s); yading@11: rt->state = RTSP_STATE_PAUSED; yading@11: } else if (methodcode == OPTIONS) { yading@11: ret = rtsp_read_options(s); yading@11: } else if (methodcode == RECORD) { yading@11: ret = rtsp_read_record(s); yading@11: if (!ret) yading@11: return 0; // We are ready for streaming yading@11: } else if (methodcode == SETUP) yading@11: ret = rtsp_read_setup(s, host, uri); yading@11: if (ret) { yading@11: ffurl_close(rt->rtsp_hd); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: static int rtsp_probe(AVProbeData *p) yading@11: { yading@11: if (av_strstart(p->filename, "rtsp:", NULL)) yading@11: return AVPROBE_SCORE_MAX; yading@11: return 0; yading@11: } yading@11: yading@11: static int rtsp_read_header(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: int ret; yading@11: yading@11: if (rt->initial_timeout > 0) yading@11: rt->rtsp_flags |= RTSP_FLAG_LISTEN; yading@11: yading@11: if (rt->rtsp_flags & RTSP_FLAG_LISTEN) { yading@11: ret = rtsp_listen(s); yading@11: if (ret) yading@11: return ret; yading@11: } else { yading@11: ret = ff_rtsp_connect(s); yading@11: if (ret) yading@11: return ret; yading@11: yading@11: rt->real_setup_cache = !s->nb_streams ? NULL : yading@11: av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache)); yading@11: if (!rt->real_setup_cache && s->nb_streams) yading@11: return AVERROR(ENOMEM); yading@11: rt->real_setup = rt->real_setup_cache + s->nb_streams; yading@11: yading@11: if (rt->initial_pause) { yading@11: /* do not start immediately */ yading@11: } else { yading@11: if (rtsp_read_play(s) < 0) { yading@11: ff_rtsp_close_streams(s); yading@11: ff_rtsp_close_connections(s); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: } yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: int ff_rtsp_tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, yading@11: uint8_t *buf, int buf_size) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: int id, len, i, ret; yading@11: RTSPStream *rtsp_st; yading@11: yading@11: av_dlog(s, "tcp_read_packet:\n"); yading@11: redo: yading@11: for (;;) { yading@11: RTSPMessageHeader reply; yading@11: yading@11: ret = ff_rtsp_read_reply(s, &reply, NULL, 1, NULL); yading@11: if (ret < 0) yading@11: return ret; yading@11: if (ret == 1) /* received '$' */ yading@11: break; yading@11: /* XXX: parse message */ yading@11: if (rt->state != RTSP_STATE_STREAMING) yading@11: return 0; yading@11: } yading@11: ret = ffurl_read_complete(rt->rtsp_hd, buf, 3); yading@11: if (ret != 3) yading@11: return -1; yading@11: id = buf[0]; yading@11: len = AV_RB16(buf + 1); yading@11: av_dlog(s, "id=%d len=%d\n", id, len); yading@11: if (len > buf_size || len < 8) yading@11: goto redo; yading@11: /* get the data */ yading@11: ret = ffurl_read_complete(rt->rtsp_hd, buf, len); yading@11: if (ret != len) yading@11: return -1; yading@11: if (rt->transport == RTSP_TRANSPORT_RDT && yading@11: ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL) < 0) yading@11: return -1; yading@11: yading@11: /* find the matching stream */ yading@11: for (i = 0; i < rt->nb_rtsp_streams; i++) { yading@11: rtsp_st = rt->rtsp_streams[i]; yading@11: if (id >= rtsp_st->interleaved_min && yading@11: id <= rtsp_st->interleaved_max) yading@11: goto found; yading@11: } yading@11: goto redo; yading@11: found: yading@11: *prtsp_st = rtsp_st; yading@11: return len; yading@11: } yading@11: yading@11: static int resetup_tcp(AVFormatContext *s) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: char host[1024]; yading@11: int port; yading@11: yading@11: av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, yading@11: s->filename); yading@11: ff_rtsp_undo_setup(s); yading@11: return ff_rtsp_make_setup_request(s, host, port, RTSP_LOWER_TRANSPORT_TCP, yading@11: rt->real_challenge); yading@11: } yading@11: yading@11: static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: int ret; yading@11: RTSPMessageHeader reply1, *reply = &reply1; yading@11: char cmd[1024]; yading@11: yading@11: retry: yading@11: if (rt->server_type == RTSP_SERVER_REAL) { yading@11: int i; yading@11: yading@11: for (i = 0; i < s->nb_streams; i++) yading@11: rt->real_setup[i] = s->streams[i]->discard; yading@11: yading@11: if (!rt->need_subscription) { yading@11: if (memcmp (rt->real_setup, rt->real_setup_cache, yading@11: sizeof(enum AVDiscard) * s->nb_streams)) { yading@11: snprintf(cmd, sizeof(cmd), yading@11: "Unsubscribe: %s\r\n", yading@11: rt->last_subscription); yading@11: ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, yading@11: cmd, reply, NULL); yading@11: if (reply->status_code != RTSP_STATUS_OK) yading@11: return AVERROR_INVALIDDATA; yading@11: rt->need_subscription = 1; yading@11: } yading@11: } yading@11: yading@11: if (rt->need_subscription) { yading@11: int r, rule_nr, first = 1; yading@11: yading@11: memcpy(rt->real_setup_cache, rt->real_setup, yading@11: sizeof(enum AVDiscard) * s->nb_streams); yading@11: rt->last_subscription[0] = 0; yading@11: yading@11: snprintf(cmd, sizeof(cmd), yading@11: "Subscribe: "); yading@11: for (i = 0; i < rt->nb_rtsp_streams; i++) { yading@11: rule_nr = 0; yading@11: for (r = 0; r < s->nb_streams; r++) { yading@11: if (s->streams[r]->id == i) { yading@11: if (s->streams[r]->discard != AVDISCARD_ALL) { yading@11: if (!first) yading@11: av_strlcat(rt->last_subscription, ",", yading@11: sizeof(rt->last_subscription)); yading@11: ff_rdt_subscribe_rule( yading@11: rt->last_subscription, yading@11: sizeof(rt->last_subscription), i, rule_nr); yading@11: first = 0; yading@11: } yading@11: rule_nr++; yading@11: } yading@11: } yading@11: } yading@11: av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription); yading@11: ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, yading@11: cmd, reply, NULL); yading@11: if (reply->status_code != RTSP_STATUS_OK) yading@11: return AVERROR_INVALIDDATA; yading@11: rt->need_subscription = 0; yading@11: yading@11: if (rt->state == RTSP_STATE_STREAMING) yading@11: rtsp_read_play (s); yading@11: } yading@11: } yading@11: yading@11: ret = ff_rtsp_fetch_packet(s, pkt); yading@11: if (ret < 0) { yading@11: if (ret == AVERROR(ETIMEDOUT) && !rt->packets) { yading@11: if (rt->lower_transport == RTSP_LOWER_TRANSPORT_UDP && yading@11: rt->lower_transport_mask & (1 << RTSP_LOWER_TRANSPORT_TCP)) { yading@11: RTSPMessageHeader reply1, *reply = &reply1; yading@11: av_log(s, AV_LOG_WARNING, "UDP timeout, retrying with TCP\n"); yading@11: if (rtsp_read_pause(s) != 0) yading@11: return -1; yading@11: // TEARDOWN is required on Real-RTSP, but might make yading@11: // other servers close the connection. yading@11: if (rt->server_type == RTSP_SERVER_REAL) yading@11: ff_rtsp_send_cmd(s, "TEARDOWN", rt->control_uri, NULL, yading@11: reply, NULL); yading@11: rt->session_id[0] = '\0'; yading@11: if (resetup_tcp(s) == 0) { yading@11: rt->state = RTSP_STATE_IDLE; yading@11: rt->need_subscription = 1; yading@11: if (rtsp_read_play(s) != 0) yading@11: return -1; yading@11: goto retry; yading@11: } yading@11: } yading@11: } yading@11: return ret; yading@11: } yading@11: rt->packets++; yading@11: yading@11: if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) { yading@11: /* send dummy request to keep TCP connection alive */ yading@11: if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2 || yading@11: rt->auth_state.stale) { yading@11: if (rt->server_type == RTSP_SERVER_WMS || yading@11: (rt->server_type != RTSP_SERVER_REAL && yading@11: rt->get_parameter_supported)) { yading@11: ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL); yading@11: } else { yading@11: ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL); yading@11: } yading@11: /* The stale flag should be reset when creating the auth response in yading@11: * ff_rtsp_send_cmd_async, but reset it here just in case we never yading@11: * called the auth code (if we didn't have any credentials set). */ yading@11: rt->auth_state.stale = 0; yading@11: } yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int rtsp_read_seek(AVFormatContext *s, int stream_index, yading@11: int64_t timestamp, int flags) yading@11: { yading@11: RTSPState *rt = s->priv_data; yading@11: yading@11: rt->seek_timestamp = av_rescale_q(timestamp, yading@11: s->streams[stream_index]->time_base, yading@11: AV_TIME_BASE_Q); yading@11: switch(rt->state) { yading@11: default: yading@11: case RTSP_STATE_IDLE: yading@11: break; yading@11: case RTSP_STATE_STREAMING: yading@11: if (rtsp_read_pause(s) != 0) yading@11: return -1; yading@11: rt->state = RTSP_STATE_SEEKING; yading@11: if (rtsp_read_play(s) != 0) yading@11: return -1; yading@11: break; yading@11: case RTSP_STATE_PAUSED: yading@11: rt->state = RTSP_STATE_IDLE; yading@11: break; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: static const AVClass rtsp_demuxer_class = { yading@11: .class_name = "RTSP demuxer", yading@11: .item_name = av_default_item_name, yading@11: .option = ff_rtsp_options, yading@11: .version = LIBAVUTIL_VERSION_INT, yading@11: }; yading@11: yading@11: AVInputFormat ff_rtsp_demuxer = { yading@11: .name = "rtsp", yading@11: .long_name = NULL_IF_CONFIG_SMALL("RTSP input"), yading@11: .priv_data_size = sizeof(RTSPState), yading@11: .read_probe = rtsp_probe, yading@11: .read_header = rtsp_read_header, yading@11: .read_packet = rtsp_read_packet, yading@11: .read_close = rtsp_read_close, yading@11: .read_seek = rtsp_read_seek, yading@11: .flags = AVFMT_NOFILE, yading@11: .read_play = rtsp_read_play, yading@11: .read_pause = rtsp_read_pause, yading@11: .priv_class = &rtsp_demuxer_class, yading@11: };