rtsp: Add listen mode

This makes the RTSP demuxer act as a server, listening for an
incoming connection.

Signed-off-by: Martin Storsjö <martin@martin.st>
This commit is contained in:
Jordi Ortiz 2012-07-10 19:36:11 +02:00 committed by Martin Storsjö
parent 6e71c1202b
commit a8ad6ffafe
7 changed files with 601 additions and 39 deletions

View File

@ -31,6 +31,7 @@ version <next>:
- join audio filter
- audio channel mapping filter
- Microsoft ATC Screen decoder
- RTSP listen mode
version 0.8:

View File

@ -347,6 +347,8 @@ Flags for @code{rtsp_flags}:
@table @option
@item filter_src
Accept packets only from negotiated peer address and port.
@item listen
Act as a server, listening for an incoming connection.
@end table
When receiving data over UDP, the demuxer tries to reorder received packets
@ -379,6 +381,12 @@ To send a stream in realtime to a RTSP server, for others to watch:
avconv -re -i @var{input} -f rtsp -muxdelay 0.1 rtsp://server/live.sdp
@end example
To receive a stream in realtime:
@example
avconv -rtsp_flags listen -i rtsp://ownaddress/live.sdp @var{output}
@end example
@section sap
Session Announcement Protocol (RFC 2974). This is not technically a

View File

@ -63,7 +63,8 @@
#define RTSP_FLAG_OPTS(name, longname) \
{ name, longname, OFFSET(rtsp_flags), AV_OPT_TYPE_FLAGS, {0}, INT_MIN, INT_MAX, DEC, "rtsp_flags" }, \
{ "filter_src", "Only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" }
{ "filter_src", "Only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" }, \
{ "listen", "Wait for incoming connections", 0, AV_OPT_TYPE_CONST, {RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" }
#define RTSP_MEDIATYPE_OPTS(name, longname) \
{ name, longname, OFFSET(media_type_mask), AV_OPT_TYPE_FLAGS, { (1 << (AVMEDIA_TYPE_DATA+1)) - 1 }, INT_MIN, INT_MAX, DEC, "allowed_media_types" }, \
@ -83,6 +84,7 @@ const AVOption ff_rtsp_options[] = {
RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"),
{ "min_port", "Minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC },
{ "max_port", "Maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC },
{ "timeout", "Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies flag listen", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {-1}, INT_MIN, INT_MAX, DEC },
{ NULL },
};
@ -1714,14 +1716,24 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
}
#if CONFIG_RTSP_DEMUXER
if (tcp_fd != -1 && p[0].revents & POLLIN) {
RTSPMessageHeader reply;
ret = ff_rtsp_read_reply(s, &reply, NULL, 0, NULL);
if (ret < 0)
return ret;
/* XXX: parse message */
if (rt->state != RTSP_STATE_STREAMING)
return 0;
if (rt->rtsp_flags & RTSP_FLAG_LISTEN) {
if (rt->state == RTSP_STATE_STREAMING) {
if (!ff_rtsp_parse_streaming_commands(s))
return AVERROR_EOF;
else
av_log(s, AV_LOG_WARNING,
"Unable to answer to TEARDOWN\n");
} else
return 0;
} else {
RTSPMessageHeader reply;
ret = ff_rtsp_read_reply(s, &reply, NULL, 0, NULL);
if (ret < 0)
return ret;
/* XXX: parse message */
if (rt->state != RTSP_STATE_STREAMING)
return 0;
}
}
#endif
} else if (n == 0 && ++timeout_cnt >= MAX_TIMEOUTS) {

View File

@ -372,11 +372,17 @@ typedef struct RTSPState {
* Minimum and maximum local UDP ports.
*/
int rtp_port_min, rtp_port_max;
/**
* Timeout to wait for incoming connections.
*/
int initial_timeout;
} RTSPState;
#define RTSP_FLAG_FILTER_SRC 0x1 /**< Filter incoming UDP packets -
receive packets only from the right
source address and port. */
#define RTSP_FLAG_LISTEN 0x2 /**< Wait for incoming connections. */
/**
* Describe a single stream, as identified by a single m= line block in the
@ -528,6 +534,12 @@ int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply);
*/
int ff_rtsp_setup_output_streams(AVFormatContext *s, const char *addr);
/**
* Parse RTSP commands (OPTIONS, PAUSE and TEARDOWN) during streaming in
* listen mode.
*/
int ff_rtsp_parse_streaming_commands(AVFormatContext *s);
/**
* Parse an SDP description of streams by populating an RTSPState struct
* within the AVFormatContext; also allocate the RTP streams and the

View File

@ -37,4 +37,18 @@ RTSP_STATUS_SERVICE =503, /**< Service Unavailable */
RTSP_STATUS_VERSION =505, /**< RTSP Version not supported */
};
enum RTSPMethod {
DESCRIBE,
ANNOUNCE,
OPTIONS,
SETUP,
PLAY,
PAUSE,
TEARDOWN,
GET_PARAMETER,
SET_PARAMETER,
REDIRECT,
RECORD,
UNKNOWN = -1,
};
#endif /* AVFORMAT_RTSPCODES_H */

View File

@ -22,6 +22,7 @@
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/random_seed.h"
#include "avformat.h"
#include "internal.h"
@ -31,11 +32,30 @@
#include "rdt.h"
#include "url.h"
static const struct RTSPStatusMessage {
enum RTSPStatusCode code;
const char *message;
} status_messages[] = {
{ RTSP_STATUS_OK, "OK" },
{ RTSP_STATUS_METHOD, "Method Not Allowed" },
{ RTSP_STATUS_BANDWIDTH, "Not Enough Bandwidth" },
{ RTSP_STATUS_SESSION, "Session Not Found" },
{ RTSP_STATUS_STATE, "Method Not Valid in This State" },
{ RTSP_STATUS_AGGREGATE, "Aggregate operation not allowed" },
{ RTSP_STATUS_ONLY_AGGREGATE, "Only aggregate operation allowed" },
{ RTSP_STATUS_TRANSPORT, "Unsupported transport" },
{ RTSP_STATUS_INTERNAL, "Internal Server Error" },
{ RTSP_STATUS_SERVICE, "Service Unavailable" },
{ RTSP_STATUS_VERSION, "RTSP Version not supported" },
{ 0, "NULL" }
};
static int rtsp_read_close(AVFormatContext *s)
{
RTSPState *rt = s->priv_data;
ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL);
if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN))
ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL);
ff_rtsp_close_streams(s);
ff_rtsp_close_connections(s);
@ -45,6 +65,429 @@ static int rtsp_read_close(AVFormatContext *s)
return 0;
}
static inline int read_line(AVFormatContext *s, char *rbuf, const int rbufsize,
int *rbuflen)
{
RTSPState *rt = s->priv_data;
int idx = 0;
int ret = 0;
*rbuflen = 0;
do {
ret = ffurl_read_complete(rt->rtsp_hd, rbuf + idx, 1);
if (ret < 0)
return ret;
if (rbuf[idx] == '\r') {
/* Ignore */
} else if (rbuf[idx] == '\n') {
rbuf[idx] = '\0';
*rbuflen = idx;
return 0;
} else
idx++;
} while (idx < rbufsize);
av_log(s, AV_LOG_ERROR, "Message too long\n");
return AVERROR(EIO);
}
static int rtsp_send_reply(AVFormatContext *s, enum RTSPStatusCode code,
const char *extracontent, uint16_t seq)
{
RTSPState *rt = s->priv_data;
char message[4096];
int index = 0;
while (status_messages[index].code) {
if (status_messages[index].code == code) {
snprintf(message, sizeof(message), "RTSP/1.0 %d %s\r\n",
code, status_messages[index].message);
break;
}
index++;
}
if (!status_messages[index].code)
return AVERROR(EINVAL);
av_strlcatf(message, sizeof(message), "CSeq: %d\r\n", seq);
av_strlcatf(message, sizeof(message), "Server: %s\r\n", LIBAVFORMAT_IDENT);
if (extracontent)
av_strlcat(message, extracontent, sizeof(message));
av_strlcat(message, "\r\n", sizeof(message));
av_dlog(s, "Sending response:\n%s", message);
ffurl_write(rt->rtsp_hd, message, strlen(message));
return 0;
}
static inline int check_sessionid(AVFormatContext *s,
RTSPMessageHeader *request)
{
RTSPState *rt = s->priv_data;
unsigned char *session_id = rt->session_id;
if (!session_id[0]) {
av_log(s, AV_LOG_WARNING, "There is no session-id at the moment\n");
return 0;
}
if (strcmp(session_id, request->session_id)) {
av_log(s, AV_LOG_ERROR, "Unexpected session-id %s\n",
request->session_id);
rtsp_send_reply(s, RTSP_STATUS_SESSION, NULL, request->seq);
return AVERROR_STREAM_NOT_FOUND;
}
return 0;
}
static inline int rtsp_read_request(AVFormatContext *s,
RTSPMessageHeader *request,
const char *method)
{
RTSPState *rt = s->priv_data;
char rbuf[1024];
int rbuflen, ret;
do {
ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen);
if (ret)
return ret;
if (rbuflen > 1) {
av_dlog(s, "Parsing[%d]: %s\n", rbuflen, rbuf);
ff_rtsp_parse_line(request, rbuf, rt, method);
}
} while (rbuflen > 0);
if (request->seq != rt->seq + 1) {
av_log(s, AV_LOG_ERROR, "Unexpected Sequence number %d\n",
request->seq);
return AVERROR(EINVAL);
}
if (rt->session_id[0] && strcmp(method, "OPTIONS")) {
ret = check_sessionid(s, request);
if (ret)
return ret;
}
return 0;
}
static int rtsp_read_announce(AVFormatContext *s)
{
RTSPState *rt = s->priv_data;
RTSPMessageHeader request = { 0 };
char sdp[4096];
int ret;
ret = rtsp_read_request(s, &request, "ANNOUNCE");
if (ret)
return ret;
rt->seq++;
if (strcmp(request.content_type, "application/sdp")) {
av_log(s, AV_LOG_ERROR, "Unexpected content type %s\n",
request.content_type);
rtsp_send_reply(s, RTSP_STATUS_SERVICE, NULL, request.seq);
return AVERROR_OPTION_NOT_FOUND;
}
if (request.content_length && request.content_length < sizeof(sdp) - 1) {
/* Read SDP */
if (ffurl_read_complete(rt->rtsp_hd, sdp, request.content_length)
< request.content_length) {
av_log(s, AV_LOG_ERROR,
"Unable to get complete SDP Description in ANNOUNCE\n");
rtsp_send_reply(s, RTSP_STATUS_INTERNAL, NULL, request.seq);
return AVERROR(EIO);
}
sdp[request.content_length] = '\0';
av_log(s, AV_LOG_VERBOSE, "SDP: %s\n", sdp);
ret = ff_sdp_parse(s, sdp);
if (ret)
return ret;
rtsp_send_reply(s, RTSP_STATUS_OK, NULL, request.seq);
return 0;
}
av_log(s, AV_LOG_ERROR,
"Content-Length header value exceeds sdp allocated buffer (4KB)\n");
rtsp_send_reply(s, RTSP_STATUS_INTERNAL,
"Content-Length exceeds buffer size", request.seq);
return AVERROR(EIO);
}
static int rtsp_read_options(AVFormatContext *s)
{
RTSPState *rt = s->priv_data;
RTSPMessageHeader request = { 0 };
int ret = 0;
/* Parsing headers */
ret = rtsp_read_request(s, &request, "OPTIONS");
if (ret)
return ret;
rt->seq++;
/* Send Reply */
rtsp_send_reply(s, RTSP_STATUS_OK,
"Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, RECORD\r\n",
request.seq);
return 0;
}
static int rtsp_read_setup(AVFormatContext *s, char* host, char *controlurl)
{
RTSPState *rt = s->priv_data;
RTSPMessageHeader request = { 0 };
int ret = 0;
char url[1024];
RTSPStream *rtsp_st;
char responseheaders[1024];
int localport = -1;
int transportidx = 0;
int streamid = 0;
ret = rtsp_read_request(s, &request, "SETUP");
if (ret)
return ret;
rt->seq++;
if (!request.nb_transports) {
av_log(s, AV_LOG_ERROR, "No transport defined in SETUP\n");
return AVERROR_INVALIDDATA;
}
for (transportidx = 0; transportidx < request.nb_transports;
transportidx++) {
if (!request.transports[transportidx].mode_record ||
(request.transports[transportidx].lower_transport !=
RTSP_LOWER_TRANSPORT_UDP &&
request.transports[transportidx].lower_transport !=
RTSP_LOWER_TRANSPORT_TCP)) {
av_log(s, AV_LOG_ERROR, "mode=record/receive not set or transport"
" protocol not supported (yet)\n");
return AVERROR_INVALIDDATA;
}
}
if (request.nb_transports > 1)
av_log(s, AV_LOG_WARNING, "More than one transport not supported, "
"using first of all\n");
for (streamid = 0; streamid < rt->nb_rtsp_streams; streamid++) {
if (!strcmp(rt->rtsp_streams[streamid]->control_url,
controlurl))
break;
}
if (streamid == rt->nb_rtsp_streams) {
av_log(s, AV_LOG_ERROR, "Unable to find requested track\n");
return AVERROR_STREAM_NOT_FOUND;
}
rtsp_st = rt->rtsp_streams[streamid];
localport = rt->rtp_port_min;
if (request.transports[0].lower_transport == RTSP_LOWER_TRANSPORT_TCP) {
rt->lower_transport = RTSP_LOWER_TRANSPORT_TCP;
if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) {
rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq);
return ret;
}
rtsp_st->interleaved_min = request.transports[0].interleaved_min;
rtsp_st->interleaved_max = request.transports[0].interleaved_max;
snprintf(responseheaders, sizeof(responseheaders), "Transport: "
"RTP/AVP/TCP;unicast;mode=receive;interleaved=%d-%d"
"\r\n", request.transports[0].interleaved_min,
request.transports[0].interleaved_max);
} else {
do {
ff_url_join(url, sizeof(url), "rtp", NULL, host, localport, NULL);
av_dlog(s, "Opening: %s", url);
ret = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
&s->interrupt_callback, NULL);
if (ret)
localport += 2;
} while (ret || localport > rt->rtp_port_max);
if (localport > rt->rtp_port_max) {
rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq);
return ret;
}
av_dlog(s, "Listening on: %d",
ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle));
if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) {
rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq);
return ret;
}
localport = ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle);
snprintf(responseheaders, sizeof(responseheaders), "Transport: "
"RTP/AVP/UDP;unicast;mode=receive;source=%s;"
"client_port=%d-%d;server_port=%d-%d\r\n",
host, request.transports[0].client_port_min,
request.transports[0].client_port_max, localport,
localport + 1);
}
/* Establish sessionid if not previously set */
/* Put this in a function? */
/* RFC 2326: session id must be at least 8 digits */
while (strlen(rt->session_id) < 8)
av_strlcatf(rt->session_id, 512, "%u", av_get_random_seed());
av_strlcatf(responseheaders, sizeof(responseheaders), "Session: %s\r\n",
rt->session_id);
/* Send Reply */
rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq);
rt->state = RTSP_STATE_PAUSED;
return 0;
}
static int rtsp_read_record(AVFormatContext *s)
{
RTSPState *rt = s->priv_data;
RTSPMessageHeader request = { 0 };
int ret = 0;
char responseheaders[1024];
ret = rtsp_read_request(s, &request, "RECORD");
if (ret)
return ret;
ret = check_sessionid(s, &request);
if (ret)
return ret;
rt->seq++;
snprintf(responseheaders, sizeof(responseheaders), "Session: %s\r\n",
rt->session_id);
rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq);
rt->state = RTSP_STATE_STREAMING;
return 0;
}
static inline int parse_command_line(AVFormatContext *s, const char *line,
int linelen, char *uri, int urisize,
char *method, int methodsize,
enum RTSPMethod *methodcode)
{
RTSPState *rt = s->priv_data;
const char *linept, *searchlinept;
linept = strchr(line, ' ');
if (linept - line > methodsize - 1) {
av_log(s, AV_LOG_ERROR, "Method string too long\n");
return AVERROR(EIO);
}
memcpy(method, line, linept - line);
method[linept - line] = '\0';
linept++;
if (!strcmp(method, "ANNOUNCE"))
*methodcode = ANNOUNCE;
else if (!strcmp(method, "OPTIONS"))
*methodcode = OPTIONS;
else if (!strcmp(method, "RECORD"))
*methodcode = RECORD;
else if (!strcmp(method, "SETUP"))
*methodcode = SETUP;
else if (!strcmp(method, "PAUSE"))
*methodcode = PAUSE;
else if (!strcmp(method, "TEARDOWN"))
*methodcode = TEARDOWN;
else
*methodcode = UNKNOWN;
/* Check method with the state */
if (rt->state == RTSP_STATE_IDLE) {
if ((*methodcode != ANNOUNCE) && (*methodcode != OPTIONS)) {
av_log(s, AV_LOG_ERROR, "Unexpected command in Idle State %s\n",
line);
return AVERROR_PROTOCOL_NOT_FOUND;
}
} else if (rt->state == RTSP_STATE_PAUSED) {
if ((*methodcode != OPTIONS) && (*methodcode != RECORD)
&& (*methodcode != SETUP)) {
av_log(s, AV_LOG_ERROR, "Unexpected command in Paused State %s\n",
line);
return AVERROR_PROTOCOL_NOT_FOUND;
}
} else if (rt->state == RTSP_STATE_STREAMING) {
if ((*methodcode != PAUSE) && (*methodcode != OPTIONS)
&& (*methodcode != TEARDOWN)) {
av_log(s, AV_LOG_ERROR, "Unexpected command in Streaming State"
" %s\n", line);
return AVERROR_PROTOCOL_NOT_FOUND;
}
} else {
av_log(s, AV_LOG_ERROR, "Unexpected State [%d]\n", rt->state);
return AVERROR_BUG;
}
searchlinept = strchr(linept, ' ');
if (searchlinept == NULL) {
av_log(s, AV_LOG_ERROR, "Error parsing message URI\n");
return AVERROR_INVALIDDATA;
}
if (searchlinept - linept > urisize - 1) {
av_log(s, AV_LOG_ERROR, "uri string length exceeded buffer size\n");
return AVERROR(EIO);
}
memcpy(uri, linept, searchlinept - linept);
uri[searchlinept - linept] = '\0';
if (strcmp(rt->control_uri, uri)) {
char host[128], path[512], auth[128];
int port;
char ctl_host[128], ctl_path[512], ctl_auth[128];
int ctl_port;
av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host), &port,
path, sizeof(path), uri);
av_url_split(NULL, 0, ctl_auth, sizeof(ctl_auth), ctl_host,
sizeof(ctl_host), &ctl_port, ctl_path, sizeof(ctl_path),
rt->control_uri);
if (strcmp(host, ctl_host))
av_log(s, AV_LOG_INFO, "Host %s differs from expected %s\n",
host, ctl_host);
if (strcmp(path, ctl_path) && *methodcode != SETUP)
av_log(s, AV_LOG_WARNING, "WARNING: Path %s differs from expected"
" %s\n", path, ctl_path);
if (*methodcode == ANNOUNCE) {
av_log(s, AV_LOG_INFO,
"Updating control URI to %s\n", uri);
strcpy(rt->control_uri, uri);
}
}
linept = searchlinept + 1;
if (!av_strstart(linept, "RTSP/1.0", NULL)) {
av_log(s, AV_LOG_ERROR, "Error parsing protocol or version\n");
return AVERROR_PROTOCOL_NOT_FOUND;
}
return 0;
}
int ff_rtsp_parse_streaming_commands(AVFormatContext *s)
{
RTSPState *rt = s->priv_data;
unsigned char rbuf[4096];
unsigned char method[10];
char uri[500];
int ret;
int rbuflen = 0;
RTSPMessageHeader request = { 0 };
enum RTSPMethod methodcode;
ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen);
if (ret < 0)
return ret;
ret = parse_command_line(s, rbuf, rbuflen, uri, sizeof(uri), method,
sizeof(method), &methodcode);
if (ret) {
av_log(s, AV_LOG_ERROR, "RTSP: Unexpected Command\n");
return ret;
}
ret = rtsp_read_request(s, &request, method);
if (ret)
return ret;
rt->seq++;
if (methodcode == PAUSE) {
rt->state = RTSP_STATE_PAUSED;
ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq);
// TODO: Missing date header in response
} else if (methodcode == OPTIONS) {
ret = rtsp_send_reply(s, RTSP_STATUS_OK,
"Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, "
"RECORD\r\n", request.seq);
} else if (methodcode == TEARDOWN) {
rt->state = RTSP_STATE_IDLE;
ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq);
return 0;
}
return ret;
}
static int rtsp_read_play(AVFormatContext *s)
{
RTSPState *rt = s->priv_data;
@ -157,6 +600,67 @@ int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply)
return 0;
}
static int rtsp_listen(AVFormatContext *s)
{
RTSPState *rt = s->priv_data;
char host[128], path[512], auth[128];
char uri[500];
int port;
char tcpname[500];
unsigned char rbuf[4096];
unsigned char method[10];
int rbuflen = 0;
int ret;
enum RTSPMethod methodcode;
/* extract hostname and port */
av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host), &port,
path, sizeof(path), s->filename);
/* ff_url_join. No authorization by now (NULL) */
ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL, host,
port, "%s", path);
/* Create TCP connection */
ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port,
"?listen&listen_timeout=%d", rt->initial_timeout * 1000);
if (ret = ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
&s->interrupt_callback, NULL)) {
av_log(s, AV_LOG_ERROR, "Unable to open RTSP for listening\n");
return ret;
}
rt->state = RTSP_STATE_IDLE;
rt->rtsp_hd_out = rt->rtsp_hd;
for (;;) { /* Wait for incoming RTSP messages */
ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen);
if (ret < 0)
return ret;
ret = parse_command_line(s, rbuf, rbuflen, uri, sizeof(uri), method,
sizeof(method), &methodcode);
if (ret) {
av_log(s, AV_LOG_ERROR, "RTSP: Unexpected Command\n");
return ret;
}
if (methodcode == ANNOUNCE) {
ret = rtsp_read_announce(s);
rt->state = RTSP_STATE_PAUSED;
} else if (methodcode == OPTIONS) {
ret = rtsp_read_options(s);
} else if (methodcode == RECORD) {
ret = rtsp_read_record(s);
if (!ret)
return 0; // We are ready for streaming
} else if (methodcode == SETUP)
ret = rtsp_read_setup(s, host, uri);
if (ret) {
ffurl_close(rt->rtsp_hd);
return AVERROR_INVALIDDATA;
}
}
return 0;
}
static int rtsp_probe(AVProbeData *p)
{
if (av_strstart(p->filename, "rtsp:", NULL))
@ -169,23 +673,32 @@ static int rtsp_read_header(AVFormatContext *s)
RTSPState *rt = s->priv_data;
int ret;
ret = ff_rtsp_connect(s);
if (ret)
return ret;
if (rt->initial_timeout > 0)
rt->rtsp_flags |= RTSP_FLAG_LISTEN;
rt->real_setup_cache = !s->nb_streams ? NULL :
av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache));
if (!rt->real_setup_cache && s->nb_streams)
return AVERROR(ENOMEM);
rt->real_setup = rt->real_setup_cache + s->nb_streams;
if (rt->initial_pause) {
/* do not start immediately */
if (rt->rtsp_flags & RTSP_FLAG_LISTEN) {
ret = rtsp_listen(s);
if (ret)
return ret;
} else {
if (rtsp_read_play(s) < 0) {
ff_rtsp_close_streams(s);
ff_rtsp_close_connections(s);
return AVERROR_INVALIDDATA;
ret = ff_rtsp_connect(s);
if (ret)
return ret;
rt->real_setup_cache = !s->nb_streams ? NULL :
av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache));
if (!rt->real_setup_cache && s->nb_streams)
return AVERROR(ENOMEM);
rt->real_setup = rt->real_setup_cache + s->nb_streams;
if (rt->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;
}
}
}
@ -349,20 +862,22 @@ retry:
}
rt->packets++;
/* send dummy request to keep TCP connection alive */
if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2 ||
rt->auth_state.stale) {
if (rt->server_type == RTSP_SERVER_WMS ||
(rt->server_type != RTSP_SERVER_REAL &&
rt->get_parameter_supported)) {
ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL);
} else {
ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL);
if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) {
/* send dummy request to keep TCP connection alive */
if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2 ||
rt->auth_state.stale) {
if (rt->server_type == RTSP_SERVER_WMS ||
(rt->server_type != RTSP_SERVER_REAL &&
rt->get_parameter_supported)) {
ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL);
} else {
ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL);
}
/* The stale flag should be reset when creating the auth response in
* ff_rtsp_send_cmd_async, but reset it here just in case we never
* called the auth code (if we didn't have any credentials set). */
rt->auth_state.stale = 0;
}
/* The stale flag should be reset when creating the auth response in
* ff_rtsp_send_cmd_async, but reset it here just in case we never
* called the auth code (if we didn't have any credentials set). */
rt->auth_state.stale = 0;
}
return 0;

View File

@ -30,7 +30,7 @@
#include "libavutil/avutil.h"
#define LIBAVFORMAT_VERSION_MAJOR 54
#define LIBAVFORMAT_VERSION_MINOR 6
#define LIBAVFORMAT_VERSION_MINOR 7
#define LIBAVFORMAT_VERSION_MICRO 0
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \