#define RTSP_FLAG_OPTS(name, longname) \
{ name, longname, OFFSET(rtsp_flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtsp_flags" }, \
- { "filter_src", "Only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" }, \
- { "listen", "Wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" }
+ { "filter_src", "Only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" }
#define RTSP_MEDIATYPE_OPTS(name, longname) \
{ name, longname, OFFSET(media_type_mask), AV_OPT_TYPE_FLAGS, { .i64 = (1 << (AVMEDIA_TYPE_DATA+1)) - 1 }, INT_MIN, INT_MAX, DEC, "allowed_media_types" }, \
{ "audio", "Audio", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_AUDIO}, 0, 0, DEC, "allowed_media_types" }, \
{ "data", "Data", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_DATA}, 0, 0, DEC, "allowed_media_types" }
-#define RTSP_REORDERING_OPTS() \
- { "reorder_queue_size", "Number of packets to buffer for handling of reordered packets", OFFSET(reordering_queue_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC }
+#define COMMON_OPTS() \
+ { "reorder_queue_size", "Number of packets to buffer for handling of reordered packets", OFFSET(reordering_queue_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC }, \
+ { "buffer_size", "Underlying protocol send/receive buffer size", OFFSET(buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC|ENC } \
+
const AVOption ff_rtsp_options[] = {
{ "initial_pause", "Don't start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
{ "udp_multicast", "UDP multicast", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP_MULTICAST}, 0, 0, DEC, "rtsp_transport" },
{ "http", "HTTP tunneling", 0, AV_OPT_TYPE_CONST, {.i64 = (1 << RTSP_LOWER_TRANSPORT_HTTP)}, 0, 0, DEC, "rtsp_transport" },
RTSP_FLAG_OPTS("rtsp_flags", "RTSP flags"),
+ { "listen", "Wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" },
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, {.i64 = RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC },
{ "max_port", "Maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {.i64 = 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, {.i64 = -1}, INT_MIN, INT_MAX, DEC },
- RTSP_REORDERING_OPTS(),
+ COMMON_OPTS(),
{ NULL },
};
static const AVOption sdp_options[] = {
RTSP_FLAG_OPTS("sdp_flags", "SDP flags"),
{ "custom_io", "Use custom IO", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_CUSTOM_IO}, 0, 0, DEC, "rtsp_flags" },
+ { "rtcp_to_source", "Send RTCP packets to the source address of received packets", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_RTCP_TO_SOURCE}, 0, 0, DEC, "rtsp_flags" },
RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"),
- RTSP_REORDERING_OPTS(),
+ COMMON_OPTS(),
{ NULL },
};
static const AVOption rtp_options[] = {
RTSP_FLAG_OPTS("rtp_flags", "RTP flags"),
- RTSP_REORDERING_OPTS(),
+ COMMON_OPTS(),
{ NULL },
};
+
+static AVDictionary *map_to_opts(RTSPState *rt)
+{
+ AVDictionary *opts = NULL;
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%d", rt->buffer_size);
+ av_dict_set(&opts, "buffer_size", buf, 0);
+
+ return opts;
+}
+
static void get_word_until_chars(char *buf, int buf_size,
const char *sep, const char **pp)
{
*end = AV_NOPTS_VALUE;
get_word_sep(buf, sizeof(buf), "-", &p);
- av_parse_time(start, buf, 1);
+ if (av_parse_time(start, buf, 1) < 0)
+ return;
if (*p == '-') {
p++;
get_word_sep(buf, sizeof(buf), "-", &p);
}
}
-static int get_sockaddr(const char *buf, struct sockaddr_storage *sock)
+static int get_sockaddr(AVFormatContext *s,
+ const char *buf, struct sockaddr_storage *sock)
{
struct addrinfo hints = { 0 }, *ai = NULL;
+ int ret;
+
hints.ai_flags = AI_NUMERICHOST;
- if (getaddrinfo(buf, NULL, &hints, &ai))
+ if ((ret = getaddrinfo(buf, NULL, &hints, &ai))) {
+ av_log(s, AV_LOG_ERROR, "getaddrinfo(%s): %s\n",
+ buf,
+ gai_strerror(ret));
return -1;
+ }
memcpy(sock, ai->ai_addr, FFMIN(sizeof(*sock), ai->ai_addrlen));
freeaddrinfo(ai);
return 0;
#if CONFIG_RTPDEC
static void init_rtp_handler(RTPDynamicProtocolHandler *handler,
- RTSPStream *rtsp_st, AVCodecContext *codec)
+ RTSPStream *rtsp_st, AVStream *st)
{
+ AVCodecContext *codec = st ? st->codec : NULL;
if (!handler)
return;
if (codec)
codec->codec_id = handler->codec_id;
rtsp_st->dynamic_handler = handler;
- if (handler->alloc) {
- rtsp_st->dynamic_protocol_context = handler->alloc();
+ if (st)
+ st->need_parsing = handler->need_parsing;
+ if (handler->priv_data_size) {
+ rtsp_st->dynamic_protocol_context = av_mallocz(handler->priv_data_size);
if (!rtsp_st->dynamic_protocol_context)
rtsp_st->dynamic_handler = NULL;
}
}
+static void finalize_rtp_handler_init(AVFormatContext *s, RTSPStream *rtsp_st,
+ AVStream *st)
+{
+ if (rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->init) {
+ int ret = rtsp_st->dynamic_handler->init(s, st ? st->index : -1,
+ rtsp_st->dynamic_protocol_context);
+ if (ret < 0) {
+ if (rtsp_st->dynamic_protocol_context) {
+ if (rtsp_st->dynamic_handler->close)
+ rtsp_st->dynamic_handler->close(
+ rtsp_st->dynamic_protocol_context);
+ av_free(rtsp_st->dynamic_protocol_context);
+ }
+ rtsp_st->dynamic_protocol_context = NULL;
+ rtsp_st->dynamic_handler = NULL;
+ }
+ }
+}
+
/* parse the rtpmap description: <codec_name>/<clock_rate>[/<other params>] */
static int sdp_parse_rtpmap(AVFormatContext *s,
AVStream *st, RTSPStream *rtsp_st,
if (codec->codec_id == AV_CODEC_ID_NONE) {
RTPDynamicProtocolHandler *handler =
ff_rtp_handler_find_by_name(buf, codec->codec_type);
- init_rtp_handler(handler, rtsp_st, codec);
+ init_rtp_handler(handler, rtsp_st, st);
/* 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
default:
break;
}
- if (rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->init)
- rtsp_st->dynamic_handler->init(s, st->index,
- rtsp_st->dynamic_protocol_context);
+ finalize_rtp_handler_init(s, rtsp_st, st);
return 0;
}
struct RTSPSource **default_include_source_addrs; /**< Source-specific multicast include source IP address (from SDP content) */
int nb_default_exclude_source_addrs; /**< Number of source-specific multicast exclude source IP address (from SDP content) */
struct RTSPSource **default_exclude_source_addrs; /**< Source-specific multicast exclude source IP address (from SDP content) */
+ int seen_rtpmap;
+ int seen_fmtp;
+ char delayed_fmtp[2048];
} SDPParseState;
static void copy_default_source_addrs(struct RTSPSource **addrs, int count,
}
}
+static void parse_fmtp(AVFormatContext *s, RTSPState *rt,
+ int payload_type, const char *line)
+{
+ int i;
+
+ for (i = 0; i < rt->nb_rtsp_streams; i++) {
+ RTSPStream *rtsp_st = rt->rtsp_streams[i];
+ if (rtsp_st->sdp_payload_type == payload_type &&
+ 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, line);
+ }
+ }
+}
+
static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
int letter, const char *buf)
{
char buf1[64], st_type[64];
const char *p;
enum AVMediaType codec_type;
- int payload_type, i;
+ int payload_type;
AVStream *st;
RTSPStream *rtsp_st;
RTSPSource *rtsp_src;
struct sockaddr_storage sdp_ip;
int ttl;
- av_dlog(s, "sdp: %c='%s'\n", letter, buf);
+ av_log(s, AV_LOG_TRACE, "sdp: %c='%s'\n", letter, buf);
p = buf;
if (s1->skip_media && letter != 'm')
if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6"))
return;
get_word_sep(buf1, sizeof(buf1), "/", &p);
- if (get_sockaddr(buf1, &sdp_ip))
+ if (get_sockaddr(s, buf1, &sdp_ip))
return;
ttl = 16;
if (*p == '/') {
break;
case 'm':
/* new stream */
- s1->skip_media = 0;
+ s1->skip_media = 0;
+ s1->seen_fmtp = 0;
+ s1->seen_rtpmap = 0;
codec_type = AVMEDIA_TYPE_UNKNOWN;
get_word(st_type, sizeof(st_type), &p);
if (!strcmp(st_type, "audio")) {
codec_type = AVMEDIA_TYPE_AUDIO;
} else if (!strcmp(st_type, "video")) {
codec_type = AVMEDIA_TYPE_VIDEO;
- } else if (!strcmp(st_type, "application")) {
+ } else if (!strcmp(st_type, "application") || !strcmp(st_type, "text")) {
codec_type = AVMEDIA_TYPE_DATA;
}
if (codec_type == AVMEDIA_TYPE_UNKNOWN || !(rt->media_type_mask & (1 << codec_type))) {
if (!strcmp(ff_rtp_enc_name(rtsp_st->sdp_payload_type), "MP2T")) {
/* no corresponding stream */
if (rt->transport == RTSP_TRANSPORT_RAW) {
- if (!rt->ts && CONFIG_RTPDEC)
+ if (CONFIG_RTPDEC && !rt->ts)
rt->ts = ff_mpegts_parse_open(s);
} else {
RTPDynamicProtocolHandler *handler;
handler = ff_rtp_handler_find_by_id(
rtsp_st->sdp_payload_type, AVMEDIA_TYPE_DATA);
init_rtp_handler(handler, rtsp_st, NULL);
- if (handler && handler->init)
- handler->init(s, -1, rtsp_st->dynamic_protocol_context);
+ finalize_rtp_handler_init(s, rtsp_st, NULL);
}
} else if (rt->server_type == RTSP_SERVER_WMS &&
codec_type == AVMEDIA_TYPE_DATA) {
/* Even static payload types may need a custom depacketizer */
handler = ff_rtp_handler_find_by_id(
rtsp_st->sdp_payload_type, st->codec->codec_type);
- init_rtp_handler(handler, rtsp_st, st->codec);
- if (handler && handler->init)
- handler->init(s, st->index,
- rtsp_st->dynamic_protocol_context);
+ init_rtp_handler(handler, rtsp_st, st);
+ finalize_rtp_handler_init(s, rtsp_st, st);
}
+ if (rt->default_lang[0])
+ av_dict_set(&st->metadata, "language", rt->default_lang, 0);
}
/* put a default control url */
av_strlcpy(rtsp_st->control_url, rt->control_uri,
st = s->streams[rtsp_st->stream_index];
sdp_parse_rtpmap(s, st, rtsp_st, payload_type, p);
}
+ s1->seen_rtpmap = 1;
+ if (s1->seen_fmtp) {
+ parse_fmtp(s, rt, payload_type, s1->delayed_fmtp);
+ }
} else if (av_strstart(p, "fmtp:", &p) ||
av_strstart(p, "framesize:", &p)) {
- /* NOTE: fmtp is only supported AFTER the 'a=rtpmap:xxx' tag */
// let dynamic protocol handlers have a stab at the line.
get_word(buf1, sizeof(buf1), &p);
payload_type = atoi(buf1);
- for (i = 0; i < rt->nb_rtsp_streams; i++) {
- rtsp_st = rt->rtsp_streams[i];
- if (rtsp_st->sdp_payload_type == payload_type &&
- 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);
+ if (s1->seen_rtpmap) {
+ parse_fmtp(s, rt, payload_type, buf);
+ } else {
+ s1->seen_fmtp = 1;
+ av_strlcpy(s1->delayed_fmtp, buf, sizeof(s1->delayed_fmtp));
}
} else if (av_strstart(p, "range:", &p)) {
int64_t start, end;
/* AV_NOPTS_VALUE means live broadcast (and can't seek) */
s->duration = (end == AV_NOPTS_VALUE) ?
AV_NOPTS_VALUE : end - start;
+ } else if (av_strstart(p, "lang:", &p)) {
+ if (s->nb_streams > 0) {
+ get_word(buf1, sizeof(buf1), &p);
+ rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1];
+ if (rtsp_st->stream_index >= 0) {
+ st = s->streams[rtsp_st->stream_index];
+ av_dict_set(&st->metadata, "language", buf1, 0);
+ }
+ } else
+ get_word(rt->default_lang, sizeof(rt->default_lang), &p);
} else if (av_strstart(p, "IsRealDataType:integer;",&p)) {
if (atoi(p) == 1)
rt->transport = RTSP_TRANSPORT_RDT;
}
#endif /* CONFIG_RTPDEC */
-void ff_rtsp_undo_setup(AVFormatContext *s)
+void ff_rtsp_undo_setup(AVFormatContext *s, int send_packets)
{
RTSPState *rt = s->priv_data;
int i;
AVFormatContext *rtpctx = rtsp_st->transport_priv;
av_write_trailer(rtpctx);
if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP) {
- uint8_t *ptr;
- avio_close_dyn_buf(rtpctx->pb, &ptr);
- av_free(ptr);
+ if (CONFIG_RTSP_MUXER && rtpctx->pb && send_packets)
+ ff_rtsp_tcp_write_packet(s, rtsp_st);
+ ffio_free_dyn_buf(&rtpctx->pb);
} else {
avio_close(rtpctx->pb);
}
avformat_free_context(rtpctx);
- } else if (rt->transport == RTSP_TRANSPORT_RDT && CONFIG_RTPDEC)
+ } else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RDT)
ff_rdt_parse_close(rtsp_st->transport_priv);
- else if (rt->transport == RTSP_TRANSPORT_RTP && CONFIG_RTPDEC)
+ else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RTP)
ff_rtp_parse_close(rtsp_st->transport_priv);
}
rtsp_st->transport_priv = NULL;
int i, j;
RTSPStream *rtsp_st;
- ff_rtsp_undo_setup(s);
+ ff_rtsp_undo_setup(s, 0);
for (i = 0; i < rt->nb_rtsp_streams; i++) {
rtsp_st = rt->rtsp_streams[i];
if (rtsp_st) {
- if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context)
- rtsp_st->dynamic_handler->free(
- rtsp_st->dynamic_protocol_context);
+ if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context) {
+ if (rtsp_st->dynamic_handler->close)
+ rtsp_st->dynamic_handler->close(
+ rtsp_st->dynamic_protocol_context);
+ av_free(rtsp_st->dynamic_protocol_context);
+ }
for (j = 0; j < rtsp_st->nb_include_source_addrs; j++)
av_free(rtsp_st->include_source_addrs[j]);
av_freep(&rtsp_st->include_source_addrs);
if (rt->asf_ctx) {
avformat_close_input(&rt->asf_ctx);
}
- if (rt->ts && CONFIG_RTPDEC)
+ if (CONFIG_RTPDEC && rt->ts)
ff_mpegts_parse_close(rt->ts);
av_free(rt->p);
av_free(rt->recvbuf);
if (!st)
s->ctx_flags |= AVFMTCTX_NOHEADER;
- if (s->oformat && CONFIG_RTSP_MUXER) {
- int ret = ff_rtp_chain_mux_open(&rtsp_st->transport_priv, s, st,
- rtsp_st->rtp_handle,
+ if (CONFIG_RTSP_MUXER && s->oformat) {
+ int ret = ff_rtp_chain_mux_open((AVFormatContext **)&rtsp_st->transport_priv,
+ s, st, rtsp_st->rtp_handle,
RTSP_TCP_MAX_PACKET_SIZE,
rtsp_st->stream_index);
/* Ownership of rtp_handle is passed to the rtp mux context */
rtsp_st->rtp_handle = NULL;
if (ret < 0)
return ret;
+ st->time_base = ((AVFormatContext*)rtsp_st->transport_priv)->streams[0]->time_base;
} else if (rt->transport == RTSP_TRANSPORT_RAW) {
return 0; // Don't need to open any parser here
- } else if (rt->transport == RTSP_TRANSPORT_RDT && CONFIG_RTPDEC)
+ } else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RDT)
rtsp_st->transport_priv = ff_rdt_parse_open(s, st->index,
rtsp_st->dynamic_protocol_context,
rtsp_st->dynamic_handler);
if (!rtsp_st->transport_priv) {
return AVERROR(ENOMEM);
- } else if (rt->transport == RTSP_TRANSPORT_RTP && CONFIG_RTPDEC) {
+ } else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RTP) {
if (rtsp_st->dynamic_handler) {
ff_rtp_parse_set_dynamic_protocol(rtsp_st->transport_priv,
rtsp_st->dynamic_protocol_context,
}
/* XXX: only one transport specification is parsed */
-static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p)
+static void rtsp_parse_transport(AVFormatContext *s,
+ RTSPMessageHeader *reply, const char *p)
{
char transport_protocol[16];
char profile[16];
if (*p == '=') {
p++;
get_word_sep(buf, sizeof(buf), ";,", &p);
- get_sockaddr(buf, &th->destination);
+ get_sockaddr(s, buf, &th->destination);
}
} else if (!strcmp(parameter, "source")) {
if (*p == '=') {
p++;
reply->nb_transports++;
+ if (reply->nb_transports >= RTSP_MAX_TRANSPORTS)
+ break;
}
}
handle_rtp_info(rt, url, seq, rtptime);
}
-void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf,
+void ff_rtsp_parse_line(AVFormatContext *s,
+ RTSPMessageHeader *reply, const char *buf,
RTSPState *rt, const char *method)
{
const char *p;
} else if (av_stristart(p, "Content-Length:", &p)) {
reply->content_length = strtol(p, NULL, 10);
} else if (av_stristart(p, "Transport:", &p)) {
- rtsp_parse_transport(reply, p);
+ rtsp_parse_transport(s, reply, p);
} else if (av_stristart(p, "CSeq:", &p)) {
reply->seq = strtol(p, NULL, 10);
} else if (av_stristart(p, "Range:", &p)) {
return;
len = AV_RB16(buf + 1);
- av_dlog(s, "skipping RTP packet len=%d\n", len);
+ av_log(s, AV_LOG_TRACE, "skipping RTP packet len=%d\n", len);
/* skip payload */
while (len > 0) {
q = buf;
for (;;) {
ret = ffurl_read_complete(rt->rtsp_hd, &ch, 1);
- av_dlog(s, "ret=%d c=%02x [%c]\n", ret, ch, ch);
+ av_log(s, AV_LOG_TRACE, "ret=%d c=%02x [%c]\n", ret, ch, ch);
if (ret != 1)
return AVERROR_EOF;
if (ch == '\n')
break;
- if (ch == '$') {
- /* XXX: only parse it if first char on line ? */
+ if (ch == '$' && q == buf) {
if (return_on_interleaved_data) {
return 1;
} else
}
*q = '\0';
- av_dlog(s, "line='%s'\n", buf);
+ av_log(s, AV_LOG_TRACE, "line='%s'\n", buf);
/* test if last line */
if (buf[0] == '\0')
request = 1;
}
} else {
- ff_rtsp_parse_line(reply, p, rt, method);
+ ff_rtsp_parse_line(s, reply, p, rt, method);
av_strlcat(rt->last_reply, p, sizeof(rt->last_reply));
av_strlcat(rt->last_reply, "\n", sizeof(rt->last_reply));
}
if (content_length > 0) {
/* leave some room for a trailing '\0' (useful for simple parsing) */
content = av_malloc(content_length + 1);
+ if (!content)
+ return AVERROR(ENOMEM);
ffurl_read_complete(rt->rtsp_hd, content, content_length);
content[content_length] = '\0';
}
}
ffurl_write(rt->rtsp_hd_out, ptr, strlen(ptr));
- rt->last_cmd_time = av_gettime();
+ rt->last_cmd_time = av_gettime_relative();
/* Even if the request from the server had data, it is not the data
* that the caller wants or expects. The memory could also be leaked
* if the actual following reply has content data. */
out_buf = base64buf;
}
- av_dlog(s, "Sending:\n%s--\n", buf);
+ av_log(s, AV_LOG_TRACE, "Sending:\n%s--\n", buf);
ffurl_write(rt->rtsp_hd_out, out_buf, strlen(out_buf));
if (send_content_length > 0 && send_content) {
}
ffurl_write(rt->rtsp_hd_out, send_content, send_content_length);
}
- rt->last_cmd_time = av_gettime();
+ rt->last_cmd_time = av_gettime_relative();
return 0;
}
/* first try in specified port range */
while (j <= rt->rtp_port_max) {
+ AVDictionary *opts = map_to_opts(rt);
+
ff_url_join(buf, sizeof(buf), "rtp", NULL, host, -1,
"?localport=%d", j);
/* we will use two ports per rtp stream (rtp and rtcp) */
j += 2;
- if (!ffurl_open(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL))
+ err = ffurl_open(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, &opts);
+
+ av_dict_free(&opts);
+
+ if (!err)
goto rtp_opened;
}
transport);
if (rt->accept_dynamic_rate)
av_strlcat(cmd, "x-Dynamic-Rate: 0\r\n", sizeof(cmd));
- if (i == 0 && rt->server_type == RTSP_SERVER_REAL && CONFIG_RTPDEC) {
+ if (CONFIG_RTPDEC && i == 0 && rt->server_type == RTSP_SERVER_REAL) {
char real_res[41], real_csum[9];
ff_rdt_calc_response_and_checksum(real_res, real_csum,
real_challenge);
err = AVERROR_INVALIDDATA;
goto fail;
}
- /* Try to initialize the connection state in a
- * potential NAT router by sending dummy packets.
- * RTP/RTCP dummy packets are used for RDT, too.
- */
- if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) && s->iformat &&
- CONFIG_RTPDEC)
- ff_rtp_send_punch_packets(rtsp_st->rtp_handle);
break;
}
case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: {
return 0;
fail:
- ff_rtsp_undo_setup(s);
+ ff_rtsp_undo_setup(s, 0);
return err;
}
int ff_rtsp_connect(AVFormatContext *s)
{
RTSPState *rt = s->priv_data;
- char host[1024], path[1024], tcpname[1024], cmd[2048], auth[128];
+ char proto[128], host[1024], path[1024];
+ char tcpname[1024], cmd[2048], auth[128];
+ const char *lower_rtsp_proto = "tcp";
int port, err, tcp_fd;
RTSPMessageHeader reply1 = {0}, *reply = &reply1;
int lower_transport_mask = 0;
+ int default_port = RTSP_DEFAULT_PORT;
char real_challenge[64] = "";
struct sockaddr_storage peer;
socklen_t peer_len = sizeof(peer);
rt->lower_transport_mask &= (1 << RTSP_LOWER_TRANSPORT_NB) - 1;
redirect:
- lower_transport_mask = rt->lower_transport_mask;
/* extract hostname and port */
- av_url_split(NULL, 0, auth, sizeof(auth),
+ av_url_split(proto, sizeof(proto), auth, sizeof(auth),
host, sizeof(host), &port, path, sizeof(path), s->filename);
+
+ if (!strcmp(proto, "rtsps")) {
+ lower_rtsp_proto = "tls";
+ default_port = RTSPS_DEFAULT_PORT;
+ rt->lower_transport_mask = 1 << RTSP_LOWER_TRANSPORT_TCP;
+ }
+
if (*auth) {
av_strlcpy(rt->auth, auth, sizeof(rt->auth));
}
if (port < 0)
- port = RTSP_DEFAULT_PORT;
+ port = default_port;
+
+ lower_transport_mask = rt->lower_transport_mask;
if (!lower_transport_mask)
lower_transport_mask = (1 << RTSP_LOWER_TRANSPORT_NB) - 1;
/* Construct the URI used in request; this is similar to s->filename,
* but with authentication credentials removed and RTSP specific options
* stripped out. */
- ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL,
+ ff_url_join(rt->control_uri, sizeof(rt->control_uri), proto, NULL,
host, port, "%s", path);
if (rt->control_transport == RTSP_MODE_TUNNEL) {
}
} else {
/* open the tcp connection */
- ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);
+ ff_url_join(tcpname, sizeof(tcpname), lower_rtsp_proto, NULL,
+ host, port, NULL);
if (ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
&s->interrupt_callback, NULL) < 0) {
err = AVERROR(EIO);
rt->seq = 0;
tcp_fd = ffurl_get_file_handle(rt->rtsp_hd);
+ if (tcp_fd < 0) {
+ err = tcp_fd;
+ goto fail;
+ }
if (!getpeername(tcp_fd, (struct sockaddr*) &peer, &peer_len)) {
getnameinfo((struct sockaddr*) &peer, peer_len, host, sizeof(host),
NULL, 0, NI_NUMERICHOST);
break;
}
- if (s->iformat && CONFIG_RTSP_DEMUXER)
+ if (CONFIG_RTSP_DEMUXER && s->iformat)
err = ff_rtsp_setup_input_streams(s, reply);
else if (CONFIG_RTSP_MUXER)
err = ff_rtsp_setup_output_streams(s, host);
ff_rtsp_close_connections(s);
if (reply->status_code >=300 && reply->status_code < 400 && s->iformat) {
av_strlcpy(s->filename, reply->location, sizeof(s->filename));
+ rt->session_id[0] = '\0';
av_log(s, AV_LOG_INFO, "Status %d: Redirecting to %s\n",
reply->status_code,
s->filename);
for (;;) {
if (ff_check_interrupt(&s->interrupt_callback))
return AVERROR_EXIT;
- if (wait_end && wait_end - av_gettime() < 0)
+ if (wait_end && wait_end - av_gettime_relative() < 0)
return AVERROR(EAGAIN);
max_p = 0;
if (rt->rtsp_hd) {
ret = ff_rdt_parse_packet(rt->cur_transport_priv, pkt, NULL, 0);
} else if (rt->transport == RTSP_TRANSPORT_RTP) {
ret = ff_rtp_parse_packet(rt->cur_transport_priv, pkt, NULL, 0);
- } else if (rt->ts && CONFIG_RTPDEC) {
+ } else if (CONFIG_RTPDEC && rt->ts) {
ret = ff_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf + rt->recvbuf_pos, rt->recvbuf_len - rt->recvbuf_pos);
if (ret >= 0) {
rt->recvbuf_pos += ret;
break;
case RTSP_LOWER_TRANSPORT_CUSTOM:
if (first_queue_st && rt->transport == RTSP_TRANSPORT_RTP &&
- wait_end && wait_end < av_gettime())
+ wait_end && wait_end < av_gettime_relative())
len = AVERROR(EAGAIN);
else
len = ffio_read_partial(s->pb, rt->recvbuf, RECVBUF_SIZE);
}
if (len == AVERROR(EAGAIN) && first_queue_st &&
rt->transport == RTSP_TRANSPORT_RTP) {
+ av_log(s, AV_LOG_WARNING,
+ "max delay reached. need to consume packet\n");
rtsp_st = first_queue_st;
ret = ff_rtp_parse_packet(rtsp_st->transport_priv, pkt, NULL, 0);
goto end;
return AVERROR_EOF;
}
}
- } else if (rt->ts && CONFIG_RTPDEC) {
+ } else if (CONFIG_RTPDEC && rt->ts) {
ret = ff_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf, len);
if (ret >= 0) {
if (ret < len) {
/* read the whole sdp file */
/* XXX: better loading */
content = av_malloc(SDP_MAX_SIZE);
+ if (!content)
+ return AVERROR(ENOMEM);
size = avio_read(s->pb, content, SDP_MAX_SIZE - 1);
if (size <= 0) {
av_free(content);
rtsp_st = rt->rtsp_streams[i];
if (!(rt->rtsp_flags & RTSP_FLAG_CUSTOM_IO)) {
- getnameinfo((struct sockaddr*) &rtsp_st->sdp_ip, sizeof(rtsp_st->sdp_ip),
- namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+ AVDictionary *opts = map_to_opts(rt);
+
+ err = getnameinfo((struct sockaddr*) &rtsp_st->sdp_ip,
+ sizeof(rtsp_st->sdp_ip),
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+ if (err) {
+ av_log(s, AV_LOG_ERROR, "getnameinfo: %s\n", gai_strerror(err));
+ err = AVERROR(EIO);
+ goto fail;
+ }
ff_url_join(url, sizeof(url), "rtp", NULL,
namebuf, rtsp_st->sdp_port,
- "?localport=%d&ttl=%d&connect=%d", rtsp_st->sdp_port,
- rtsp_st->sdp_ttl,
- rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0);
+ "?localport=%d&ttl=%d&connect=%d&write_to_source=%d",
+ rtsp_st->sdp_port, rtsp_st->sdp_ttl,
+ rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0,
+ rt->rtsp_flags & RTSP_FLAG_RTCP_TO_SOURCE ? 1 : 0);
append_source_addrs(url, sizeof(url), "sources",
rtsp_st->nb_include_source_addrs,
append_source_addrs(url, sizeof(url), "block",
rtsp_st->nb_exclude_source_addrs,
rtsp_st->exclude_source_addrs);
- if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL) < 0) {
+ err = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, &opts);
+
+ av_dict_free(&opts);
+
+ if (err < 0) {
err = AVERROR_INVALIDDATA;
goto fail;
}