#include "rtpdec.h"
#include "rdt.h"
+#include "rtp_asf.h"
+#include "rtp_vorbis.h"
//#define DEBUG
//#define DEBUG_RTP_TCP
static int rtsp_read_play(AVFormatContext *s);
-/* XXX: currently, the only way to change the protocols consists in
- changing this variable */
-
#if LIBAVFORMAT_VERSION_INT < (53 << 16)
int rtsp_default_protocols = (1 << RTSP_LOWER_TRANSPORT_UDP);
#endif
return 0;
}
-static int redir_isspace(int c)
-{
- return c == ' ' || c == '\t' || c == '\n' || c == '\r';
-}
-
+#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;
*pp = p;
}
-static void get_word_sep(char *buf, int buf_size, const char *sep,
- const char **pp)
+static void get_word_until_chars(char *buf, int buf_size,
+ const char *sep, const char **pp)
{
const char *p;
char *q;
p = *pp;
- if (*p == '/')
- p++;
skip_spaces(&p);
q = buf;
while (!strchr(sep, *p) && *p != '\0') {
*pp = p;
}
-static void get_word(char *buf, int buf_size, const char **pp)
+static void get_word_sep(char *buf, int buf_size, const char *sep,
+ const char **pp)
{
- const char *p;
- char *q;
+ if (**pp == '/') (*pp)++;
+ get_word_until_chars(buf, buf_size, sep, pp);
+}
- p = *pp;
- skip_spaces(&p);
- q = buf;
- while (!redir_isspace(*p) && *p != '\0') {
- if ((q - buf) < buf_size - 1)
- *q++ = *p;
- p++;
- }
- if (buf_size > 0)
- *q = '\0';
- *pp = p;
+static void get_word(char *buf, int buf_size, const char **pp)
+{
+ get_word_until_chars(buf, buf_size, SPACE_CHARS, pp);
}
/* parse the rtpmap description: <codec_name>/<clock_rate>[/<other
if (payload_type >= RTP_PT_PRIVATE) {
RTPDynamicProtocolHandler *handler= RTPFirstDynamicPayloadHandler;
while(handler) {
- if (!strcmp(buf, handler->enc_name) && (codec->codec_type == handler->codec_type)) {
+ if (!strcasecmp(buf, handler->enc_name) && (codec->codec_type == handler->codec_type)) {
codec->codec_id = handler->codec_id;
rtsp_st->dynamic_handler= handler;
if(handler->open) {
v = 1;
for(;;) {
skip_spaces(&p);
- if (p == '\0')
+ if (*p == '\0')
break;
c = toupper((unsigned char)*p++);
if (c >= '0' && c <= '9')
return len;
}
-static void sdp_parse_fmtp_config(AVCodecContext *codec, char *attr, char *value)
+static void sdp_parse_fmtp_config(AVCodecContext * codec, void *ctx,
+ char *attr, char *value)
{
switch (codec->codec_id) {
case CODEC_ID_MPEG4:
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;
hex_to_data(codec->extradata, value);
}
break;
+ case CODEC_ID_VORBIS:
+ ff_vorbis_parse_fmtp_config(codec, ctx, attr, value);
+ break;
default:
break;
}
int rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, char *value, int value_size)
{
skip_spaces(p);
- if(**p)
- {
+ if(**p) {
get_word_sep(attr, attr_size, "=", p);
if (**p == '=')
(*p)++;
static void sdp_parse_fmtp(AVStream *st, const char *p)
{
char attr[256];
- char value[4096];
+ /* Vorbis setup headers can be up to 12KB and are sent base64
+ * encoded, giving a 12KB * (4/3) = 16KB FMTP line. */
+ char value[16384];
int i;
RTSPStream *rtsp_st = st->priv_data;
while(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, attr, value);
+ 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)) {
} else if (av_strstart(p, "IsRealDataType:integer;",&p)) {
if (atoi(p) == 1)
rt->transport = RTSP_TRANSPORT_RDT;
- } else if (s->nb_streams > 0) {
- if (rt->server_type == RTSP_SERVER_REAL)
- ff_real_parse_sdp_a_line(s, s->nb_streams - 1, p);
-
- rtsp_st = s->streams[s->nb_streams - 1]->priv_data;
- if (rtsp_st->dynamic_handler &&
- rtsp_st->dynamic_handler->parse_sdp_a_line)
- rtsp_st->dynamic_handler->parse_sdp_a_line(s, s->nb_streams - 1,
- rtsp_st->dynamic_protocol_context, buf);
+ } else {
+ if (rt->server_type == RTSP_SERVER_WMS)
+ ff_wms_parse_sdp_a_line(s, p);
+ if (s->nb_streams > 0) {
+ if (rt->server_type == RTSP_SERVER_REAL)
+ ff_real_parse_sdp_a_line(s, s->nb_streams - 1, p);
+
+ rtsp_st = s->streams[s->nb_streams - 1]->priv_data;
+ if (rtsp_st->dynamic_handler &&
+ rtsp_st->dynamic_handler->parse_sdp_a_line)
+ rtsp_st->dynamic_handler->parse_sdp_a_line(s, s->nb_streams - 1,
+ rtsp_st->dynamic_protocol_context, buf);
+ }
}
break;
}
* contain long SDP lines containing complete ASF Headers (several
* kB) or arrays of MDPR (RM stream descriptor) headers plus
* "rulebooks" describing their properties. Therefore, the SDP line
- * buffer is large. */
- char buf[8192], *q;
+ * buffer is large.
+ *
+ * The Vorbis FMTP line can be up to 16KB - see sdp_parse_fmtp. */
+ char buf[16384], *q;
SDPParseState sdp_parse_state, *s1 = &sdp_parse_state;
memset(s1, 0, sizeof(SDPParseState));
get_word_sep(transport_protocol, sizeof(transport_protocol),
"/", &p);
- if (*p == '/')
- p++;
if (!strcasecmp (transport_protocol, "rtp")) {
get_word_sep(profile, sizeof(profile), "/;,", &p);
lower_transport[0] = '\0';
/* rtp/avp/<protocol> */
if (*p == '/') {
- p++;
get_word_sep(lower_transport, sizeof(lower_transport),
";,", &p);
}
/* NOTE: we do case independent match for broken servers */
p = buf;
if (av_stristart(p, "Session:", &p)) {
+ int t;
get_word_sep(reply->session_id, sizeof(reply->session_id), ";", &p);
+ if (av_stristart(p, ";timeout=", &p) &&
+ (t = strtol(p, NULL, 10)) > 0) {
+ reply->timeout = t;
+ }
} else if (av_stristart(p, "Content-Length:", &p)) {
reply->content_length = strtol(p, NULL, 10);
} else if (av_stristart(p, "Transport:", &p)) {
}
}
-static void rtsp_send_cmd(AVFormatContext *s,
- const char *cmd, RTSPMessageHeader *reply,
- unsigned char **content_ptr)
+/**
+ * Read a RTSP message from the server, or prepare to read data
+ * packets if we're reading data interleaved over the TCP/RTSP
+ * connection as well.
+ *
+ * @param s RTSP demuxer context
+ * @param reply pointer where the RTSP message header will be stored
+ * @param content_ptr pointer where the RTSP message body, if any, will
+ * be stored (length is in \p reply)
+ * @param return_on_interleaved_data whether the function may return if we
+ * encounter a data marker ('$'), which precedes data
+ * packets over interleaved TCP/RTSP connections. If this
+ * is set, this function will return 1 after encountering
+ * a '$'. If it is not set, the function will skip any
+ * data packets (if they are encountered), until a reply
+ * has been fully parsed. If no more data is available
+ * without parsing a reply, it will return an error.
+ *
+ * @returns 1 if a data packets is ready to be received, -1 on error,
+ * and 0 on success.
+ */
+static int
+rtsp_read_reply (AVFormatContext *s, RTSPMessageHeader *reply,
+ unsigned char **content_ptr, int return_on_interleaved_data)
{
RTSPState *rt = s->priv_data;
char buf[4096], buf1[1024], *q;
unsigned char ch;
const char *p;
- int content_length, line_count;
+ int ret, content_length, line_count = 0;
unsigned char *content = NULL;
memset(reply, 0, sizeof(*reply));
- rt->seq++;
- av_strlcpy(buf, cmd, sizeof(buf));
- snprintf(buf1, sizeof(buf1), "CSeq: %d\r\n", rt->seq);
- av_strlcat(buf, buf1, sizeof(buf));
- if (rt->session_id[0] != '\0' && !strstr(cmd, "\nIf-Match:")) {
- snprintf(buf1, sizeof(buf1), "Session: %s\r\n", rt->session_id);
- av_strlcat(buf, buf1, sizeof(buf));
- }
- av_strlcat(buf, "\r\n", sizeof(buf));
-#ifdef DEBUG
- printf("Sending:\n%s--\n", buf);
-#endif
- url_write(rt->rtsp_hd, buf, strlen(buf));
-
/* parse reply (XXX: use buffers) */
- line_count = 0;
rt->last_reply[0] = '\0';
for(;;) {
q = buf;
for(;;) {
- if (url_readbuf(rt->rtsp_hd, &ch, 1) != 1)
- break;
+ ret = url_readbuf(rt->rtsp_hd, &ch, 1);
+#ifdef DEBUG_RTP_TCP
+ printf("ret=%d c=%02x [%c]\n", ret, ch, ch);
+#endif
+ if (ret != 1)
+ return -1;
if (ch == '\n')
break;
if (ch == '$') {
/* XXX: only parse it if first char on line ? */
+ if (return_on_interleaved_data) {
+ return 1;
+ } else
rtsp_skip_packet(s);
} else if (ch != '\r') {
if ((q - buf) < sizeof(buf) - 1)
*content_ptr = content;
else
av_free(content);
+
+ return 0;
+}
+
+static void rtsp_send_cmd_async (AVFormatContext *s,
+ const char *cmd, RTSPMessageHeader *reply,
+ unsigned char **content_ptr)
+{
+ RTSPState *rt = s->priv_data;
+ char buf[4096], buf1[1024];
+
+ rt->seq++;
+ av_strlcpy(buf, cmd, sizeof(buf));
+ snprintf(buf1, sizeof(buf1), "CSeq: %d\r\n", rt->seq);
+ av_strlcat(buf, buf1, sizeof(buf));
+ if (rt->session_id[0] != '\0' && !strstr(cmd, "\nIf-Match:")) {
+ snprintf(buf1, sizeof(buf1), "Session: %s\r\n", rt->session_id);
+ av_strlcat(buf, buf1, sizeof(buf));
+ }
+ av_strlcat(buf, "\r\n", sizeof(buf));
+#ifdef DEBUG
+ printf("Sending:\n%s--\n", buf);
+#endif
+ url_write(rt->rtsp_hd, buf, strlen(buf));
+ rt->last_cmd_time = av_gettime();
+}
+
+static void rtsp_send_cmd (AVFormatContext *s,
+ const char *cmd, RTSPMessageHeader *reply,
+ unsigned char **content_ptr)
+{
+ rtsp_send_cmd_async(s, cmd, reply, content_ptr);
+
+ rtsp_read_reply(s, reply, content_ptr, 0);
}
}
}
av_free(rt->rtsp_streams);
+ if (rt->asf_ctx) {
+ av_close_input_stream (rt->asf_ctx);
+ rt->asf_ctx = NULL;
+ }
}
static int
else
trans_pref = "RTP/AVP";
+ /* default timeout: 1 minute */
+ rt->timeout = 60;
+
/* for each stream, make the setup request */
/* XXX: we assume the same server is used for the control of each
RTSP stream */
goto fail;
}
+ if (reply->timeout > 0)
+ rt->timeout = reply->timeout;
+
if (rt->server_type == RTSP_SERVER_REAL)
rt->need_subscription = 1;
#endif
redo:
for(;;) {
- ret = url_readbuf(rt->rtsp_hd, buf, 1);
-#ifdef DEBUG_RTP_TCP
- printf("ret=%d c=%02x [%c]\n", ret, buf[0], buf[0]);
-#endif
- if (ret != 1)
+ RTSPMessageHeader reply;
+
+ ret = rtsp_read_reply(s, &reply, NULL, 1);
+ if (ret == -1)
return -1;
- if (buf[0] == '$')
+ if (ret == 1) /* received '$' */
break;
+ /* XXX: parse message */
}
ret = url_readbuf(rt->rtsp_hd, buf, 3);
if (ret != 3)
RTSPState *rt = s->priv_data;
RTSPStream *rtsp_st;
fd_set rfds;
- int fd1, fd_max, n, i, ret;
+ int fd, fd_max, n, i, ret, tcp_fd;
struct timeval tv;
for(;;) {
if (url_interrupt_cb())
return AVERROR(EINTR);
FD_ZERO(&rfds);
- fd_max = -1;
+ if (rt->rtsp_hd) {
+ tcp_fd = fd_max = url_get_file_handle(rt->rtsp_hd);
+ FD_SET(tcp_fd, &rfds);
+ } else {
+ fd_max = 0;
+ tcp_fd = -1;
+ }
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 */
- fd1 = url_get_file_handle(rtsp_st->rtp_handle);
- if (fd1 > fd_max)
- fd_max = fd1;
- FD_SET(fd1, &rfds);
+ fd = url_get_file_handle(rtsp_st->rtp_handle);
+ if (fd > fd_max)
+ fd_max = fd;
+ FD_SET(fd, &rfds);
}
}
tv.tv_sec = 0;
for(i = 0; i < rt->nb_rtsp_streams; i++) {
rtsp_st = rt->rtsp_streams[i];
if (rtsp_st->rtp_handle) {
- fd1 = url_get_file_handle(rtsp_st->rtp_handle);
- if (FD_ISSET(fd1, &rfds)) {
+ fd = url_get_file_handle(rtsp_st->rtp_handle);
+ if (FD_ISSET(fd, &rfds)) {
ret = url_read(rtsp_st->rtp_handle, buf, buf_size);
if (ret > 0) {
*prtsp_st = rtsp_st;
}
}
}
+ if (FD_ISSET(tcp_fd, &rfds)) {
+ RTSPMessageHeader reply;
+
+ rtsp_read_reply(s, &reply, NULL, 0);
+ /* XXX: parse message */
+ }
}
}
}
RTSPStream *rtsp_st;
int ret, len;
uint8_t buf[10 * RTP_MAX_PACKET_LENGTH];
+ RTSPMessageHeader reply1, *reply = &reply1;
+ char cmd[1024];
if (rt->server_type == RTSP_SERVER_REAL) {
int i;
- RTSPMessageHeader reply1, *reply = &reply1;
enum AVDiscard cache[MAX_STREAMS];
- char cmd[1024];
for (i = 0; i < s->nb_streams; i++)
cache[i] = s->streams[i]->discard;
/* more packets may follow, so we save the RTP context */
rt->cur_transport_priv = rtsp_st->transport_priv;
}
+
+ /* 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 (rt->server_type == RTSP_SERVER_WMS) {
+ snprintf(cmd, sizeof(cmd) - 1,
+ "GET_PARAMETER %s RTSP/1.0\r\n",
+ s->filename);
+ rtsp_send_cmd_async(s, cmd, reply, NULL);
+ } else {
+ rtsp_send_cmd_async(s, "OPTIONS * RTSP/1.0\r\n",
+ reply, NULL);
+ }
+ }
+
return 0;
}
{
const char *p;
p = pd->buf;
- while (redir_isspace(*p))
- p++;
+ skip_spaces(&p);
if (av_strstart(p, "http://", NULL) ||
av_strstart(p, "rtsp://", NULL))
return AVPROBE_SCORE_MAX;