X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtsp.c;h=a931e62bdabce9610c38ee36b1c69af6c669f927;hb=629272d86f8c4dcfd6c38b9000bb6872f0a229a3;hp=a4bd40f25a322ed1c2ca7f34ac1c8c94fc708645;hpb=7b49ce2e344a5f8864d8365d57f3c6c743f0c8f7;p=ffmpeg diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index a4bd40f25a3..a931e62bdab 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -2,31 +2,29 @@ * RTSP/SDP client * Copyright (c) 2002 Fabrice Bellard. * - * This library is free software; you can redistribute it and/or + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" -#include /* for select() prototype */ #include -#include -#include -#ifndef __BEOS__ -# include -#else -# include "barpainet.h" -#endif +#include /* for select() prototype */ +#include "network.h" + +#include "rtp_internal.h" //#define DEBUG //#define DEBUG_RTP_TCP @@ -67,6 +65,9 @@ typedef struct RTSPStream { int sdp_ttl; /* IP TTL (from SDP content - not used in RTSP) */ int sdp_payload_type; /* payload type - only used in SDP */ rtp_payload_data_t rtp_payload_data; /* rtp payload parsing infos from SDP */ + + RTPDynamicProtocolHandler *dynamic_handler; ///< Only valid if it's a dynamic protocol. (This is the handler structure) + void *dynamic_protocol_context; ///< Only valid if it's a dynamic protocol. (This is any private data associated with the dynamic protocol) } RTSPStream; static int rtsp_read_play(AVFormatContext *s); @@ -76,8 +77,6 @@ static int rtsp_read_play(AVFormatContext *s); int rtsp_default_protocols = (1 << RTSP_PROTOCOL_RTP_UDP); -FFRTSPCallback *ff_rtsp_callback = NULL; - static int rtsp_probe(AVProbeData *p) { if (strstart(p->filename, "rtsp:", NULL)) @@ -140,7 +139,7 @@ static void get_word(char *buf, int buf_size, const char **pp) /* parse the rtpmap description: /[/] */ -static int sdp_parse_rtpmap(AVCodecContext *codec, int payload_type, const char *p) +static int sdp_parse_rtpmap(AVCodecContext *codec, RTSPStream *rtsp_st, int payload_type, const char *p) { char buf[256]; int i; @@ -151,12 +150,18 @@ static int sdp_parse_rtpmap(AVCodecContext *codec, int payload_type, const char see if we can handle this kind of payload */ get_word_sep(buf, sizeof(buf), "/", &p); if (payload_type >= RTP_PT_PRIVATE) { - /* We are in dynmaic payload type case ... search into AVRtpDynamicPayloadTypes[] */ - for (i = 0; AVRtpDynamicPayloadTypes[i].codec_id != CODEC_ID_NONE; ++i) - if (!strcmp(buf, AVRtpDynamicPayloadTypes[i].enc_name) && (codec->codec_type == AVRtpDynamicPayloadTypes[i].codec_type)) { - codec->codec_id = AVRtpDynamicPayloadTypes[i].codec_id; + RTPDynamicProtocolHandler *handler= RTPFirstDynamicPayloadHandler; + while(handler) { + if (!strcmp(buf, handler->enc_name) && (codec->codec_type == handler->codec_type)) { + codec->codec_id = handler->codec_id; + rtsp_st->dynamic_handler= handler; + if(handler->open) { + rtsp_st->dynamic_protocol_context= handler->open(); + } break; } + handler= handler->next; + } } else { /* We are in a standard case ( from http://www.iana.org/assignments/rtp-parameters) */ /* search into AVRtpPayloadTypes[] */ @@ -187,6 +192,8 @@ static int sdp_parse_rtpmap(AVCodecContext *codec, int payload_type, const char i = atoi(buf); if (i > 0) codec->channels = i; + // TODO: there is a bug here; if it is a mono stream, and less than 22000Hz, faad upconverts to stereo and twice the + // frequency. No problem, but the sample rate is being set here by the sdp line. Upcoming patch forthcoming. (rdm) } av_log(codec, AV_LOG_DEBUG, " audio samplerate set to : %i\n", codec->sample_rate); av_log(codec, AV_LOG_DEBUG, " audio channels set to : %i\n", codec->channels); @@ -236,7 +243,7 @@ static void sdp_parse_fmtp_config(AVCodecContext *codec, char *attr, char *value { switch (codec->codec_id) { case CODEC_ID_MPEG4: - case CODEC_ID_MPEG4AAC: + case CODEC_ID_AAC: if (!strcmp(attr, "config")) { /* decode the hexa encoded parameter */ int len = hex_to_data(NULL, value); @@ -274,6 +281,25 @@ static attrname_map_t attr_names[]= {NULL, -1, -1}, }; +/** parse the attribute line from the fmtp a line of an sdp resonse. This is broken out as a function +* because it is used in rtp_h264.c, which is forthcoming. +*/ +int rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, char *value, int value_size) +{ + skip_spaces(p); + if(**p) + { + get_word_sep(attr, attr_size, "=", p); + if (**p == '=') + (*p)++; + get_word_sep(value, value_size, ";", p); + if (**p == ';') + (*p)++; + return 1; + } + return 0; +} + /* parse a SDP line and save stream attributes */ static void sdp_parse_fmtp(AVStream *st, const char *p) { @@ -286,16 +312,8 @@ static void sdp_parse_fmtp(AVStream *st, const char *p) rtp_payload_data_t *rtp_payload_data = &rtsp_st->rtp_payload_data; /* loop on each attribute */ - for(;;) { - skip_spaces(&p); - if (*p == '\0') - break; - get_word_sep(attr, sizeof(attr), "=", &p); - if (*p == '=') - p++; - get_word_sep(value, sizeof(value), ";", &p); - if (*p == ';') - p++; + 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); /* Looking for a known attribute */ @@ -310,6 +328,32 @@ static void sdp_parse_fmtp(AVStream *st, const char *p) } } +/** Parse a string \p in the form of Range:npt=xx-xx, and determine the start + * and end time. + * Used for seeking in the rtp stream. + */ +static void rtsp_parse_range_npt(const char *p, int64_t *start, int64_t *end) +{ + char buf[256]; + + skip_spaces(&p); + if (!stristart(p, "npt=", &p)) + return; + + *start = AV_NOPTS_VALUE; + *end = AV_NOPTS_VALUE; + + get_word_sep(buf, sizeof(buf), "-", &p); + *start = parse_date(buf, 1); + if (*p == '-') { + p++; + get_word_sep(buf, sizeof(buf), "-", &p); + *end = parse_date(buf, 1); + } +// av_log(NULL, AV_LOG_DEBUG, "Range Start: %lld\n", *start); +// av_log(NULL, AV_LOG_DEBUG, "Range End: %lld\n", *end); +} + typedef struct SDPParseState { /* SDP only */ struct in_addr default_ip; @@ -438,7 +482,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, st = s->streams[i]; rtsp_st = st->priv_data; if (rtsp_st->sdp_payload_type == payload_type) { - sdp_parse_rtpmap(st->codec, payload_type, p); + sdp_parse_rtpmap(st->codec, rtsp_st, payload_type, p); } } } else if (strstart(p, "fmtp:", &p)) { @@ -449,9 +493,35 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, st = s->streams[i]; rtsp_st = st->priv_data; if (rtsp_st->sdp_payload_type == payload_type) { - sdp_parse_fmtp(st, p); + if(rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->parse_sdp_a_line) { + if(!rtsp_st->dynamic_handler->parse_sdp_a_line(st, rtsp_st->dynamic_protocol_context, buf)) { + sdp_parse_fmtp(st, p); + } + } else { + sdp_parse_fmtp(st, p); + } + } + } + } else if(strstart(p, "framesize:", &p)) { + // let dynamic protocol handlers have a stab at the line. + get_word(buf1, sizeof(buf1), &p); + payload_type = atoi(buf1); + for(i = 0; i < s->nb_streams;i++) { + st = s->streams[i]; + rtsp_st = st->priv_data; + if (rtsp_st->sdp_payload_type == payload_type) { + if(rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->parse_sdp_a_line) { + rtsp_st->dynamic_handler->parse_sdp_a_line(st, rtsp_st->dynamic_protocol_context, buf); + } } } + } else if(strstart(p, "range:", &p)) { + int64_t start, end; + + // this is so that seeking on a streamed file can work. + rtsp_parse_range_npt(p, &start, &end); + s->start_time= start; + s->duration= (end==AV_NOPTS_VALUE)?AV_NOPTS_VALUE:end-start; // AV_NOPTS_VALUE means live broadcast (and can't seek) } break; } @@ -606,26 +676,6 @@ static void rtsp_parse_transport(RTSPHeader *reply, const char *p) } } -static void rtsp_parse_range_npt(RTSPHeader *reply, const char *p) -{ - char buf[256]; - - skip_spaces(&p); - if (!stristart(p, "npt=", &p)) - return; - - reply->range_start = AV_NOPTS_VALUE; - reply->range_end = AV_NOPTS_VALUE; - - get_word_sep(buf, sizeof(buf), "-", &p); - reply->range_start = parse_date(buf, 1); - if (*p == '-') { - p++; - get_word_sep(buf, sizeof(buf), "-", &p); - reply->range_end = parse_date(buf, 1); - } -} - void rtsp_parse_line(RTSPHeader *reply, const char *buf) { const char *p; @@ -641,7 +691,7 @@ void rtsp_parse_line(RTSPHeader *reply, const char *buf) } else if (stristart(p, "CSeq:", &p)) { reply->seq = strtol(p, NULL, 10); } else if (stristart(p, "Range:", &p)) { - rtsp_parse_range_npt(reply, p); + rtsp_parse_range_npt(p, &reply->range_start, &reply->range_end); } } @@ -765,13 +815,6 @@ static void rtsp_send_cmd(AVFormatContext *s, *content_ptr = content; } -/* useful for modules: set RTSP callback function */ - -void rtsp_set_callback(FFRTSPCallback *rtsp_cb) -{ - ff_rtsp_callback = rtsp_cb; -} - /* close and free RTSP streams */ static void rtsp_close_streams(RTSPState *rt) @@ -786,6 +829,8 @@ static void rtsp_close_streams(RTSPState *rt) rtp_parse_close(rtsp_st->rtp_ctx); if (rtsp_st->rtp_handle) url_close(rtsp_st->rtp_handle); + if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context) + rtsp_st->dynamic_handler->close(rtsp_st->dynamic_protocol_context); } av_free(rtsp_st); } @@ -796,13 +841,13 @@ static int rtsp_read_header(AVFormatContext *s, AVFormatParameters *ap) { RTSPState *rt = s->priv_data; - char host[1024], path[1024], tcpname[1024], cmd[2048]; + char host[1024], path[1024], tcpname[1024], cmd[2048], *option_list, *option; URLContext *rtsp_hd; int port, i, j, ret, err; RTSPHeader reply1, *reply = &reply1; unsigned char *content = NULL; RTSPStream *rtsp_st; - int protocol_mask; + int protocol_mask = 0; AVStream *st; /* extract hostname and port */ @@ -811,6 +856,30 @@ static int rtsp_read_header(AVFormatContext *s, if (port < 0) port = RTSP_DEFAULT_PORT; + /* search for options */ + option_list = strchr(path, '?'); + if (option_list) { + /* remove the options from the path */ + *option_list++ = 0; + while(option_list) { + /* move the option pointer */ + option = option_list; + option_list = strchr(option_list, '&'); + if (option_list) + *(option_list++) = 0; + /* handle the options */ + if (strcmp(option, "udp") == 0) + protocol_mask = (1<< RTSP_PROTOCOL_RTP_UDP); + else if (strcmp(option, "multicast") == 0) + protocol_mask = (1<< RTSP_PROTOCOL_RTP_UDP_MULTICAST); + else if (strcmp(option, "tcp") == 0) + protocol_mask = (1<< RTSP_PROTOCOL_RTP_TCP); + } + } + + if (!protocol_mask) + protocol_mask = rtsp_default_protocols; + /* open the tcp connexion */ snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", host, port); if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) @@ -841,8 +910,6 @@ static int rtsp_read_header(AVFormatContext *s, goto fail; } - protocol_mask = rtsp_default_protocols; - /* for each stream, make the setup request */ /* XXX: we assume the same server is used for the control of each RTSP stream */ @@ -863,7 +930,7 @@ static int rtsp_read_header(AVFormatContext *s, if (RTSP_RTP_PORT_MIN != 0) { while(j <= RTSP_RTP_PORT_MAX) { snprintf(buf, sizeof(buf), "rtp://?localport=%d", j); - if (url_open(&rtsp_st->rtp_handle, buf, URL_RDONLY) == 0) { + if (url_open(&rtsp_st->rtp_handle, buf, URL_RDWR) == 0) { j += 2; /* we will use two port by rtp stream (rtp and rtcp) */ goto rtp_opened; } @@ -960,7 +1027,7 @@ static int rtsp_read_header(AVFormatContext *s, host, reply->transports[0].server_port_min, ttl); - if (url_open(&rtsp_st->rtp_handle, url, URL_RDONLY) < 0) { + if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { err = AVERROR_INVALIDDATA; goto fail; } @@ -973,24 +1040,19 @@ static int rtsp_read_header(AVFormatContext *s, st = s->streams[rtsp_st->stream_index]; if (!st) s->ctx_flags |= AVFMTCTX_NOHEADER; - rtsp_st->rtp_ctx = rtp_parse_open(s, st, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data); + rtsp_st->rtp_ctx = rtp_parse_open(s, st, rtsp_st->rtp_handle, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data); if (!rtsp_st->rtp_ctx) { err = AVERROR_NOMEM; goto fail; + } else { + if(rtsp_st->dynamic_handler) { + rtsp_st->rtp_ctx->dynamic_protocol_context= rtsp_st->dynamic_protocol_context; + rtsp_st->rtp_ctx->parse_packet= rtsp_st->dynamic_handler->parse_packet; + } } } - /* use callback if available to extend setup */ - if (ff_rtsp_callback) { - if (ff_rtsp_callback(RTSP_ACTION_CLIENT_SETUP, rt->session_id, - NULL, 0, rt->last_reply) < 0) { - err = AVERROR_INVALIDDATA; - goto fail; - } - } - - rt->state = RTSP_STATE_IDLE; rt->seek_timestamp = 0; /* default is to start stream at position zero */ @@ -1131,6 +1193,8 @@ static int rtsp_read_packet(AVFormatContext *s, case RTSP_PROTOCOL_RTP_UDP: case RTSP_PROTOCOL_RTP_UDP_MULTICAST: len = udp_read_packet(s, &rtsp_st, buf, sizeof(buf)); + if (rtsp_st->rtp_ctx) + rtp_check_and_send_back_rr(rtsp_st->rtp_ctx, len); break; } if (len < 0) @@ -1235,17 +1299,13 @@ static int rtsp_read_close(AVFormatContext *s) s->filename); rtsp_send_cmd(s, cmd, reply, NULL); - if (ff_rtsp_callback) { - ff_rtsp_callback(RTSP_ACTION_CLIENT_TEARDOWN, rt->session_id, - NULL, 0, NULL); - } - rtsp_close_streams(rt); url_close(rt->rtsp_hd); return 0; } -AVInputFormat rtsp_demux = { +#ifdef CONFIG_RTSP_DEMUXER +AVInputFormat rtsp_demuxer = { "rtsp", "RTSP input format", sizeof(RTSPState), @@ -1258,6 +1318,7 @@ AVInputFormat rtsp_demux = { .read_play = rtsp_read_play, .read_pause = rtsp_read_pause, }; +#endif static int sdp_probe(AVProbeData *p1) { @@ -1310,7 +1371,7 @@ static int sdp_read_header(AVFormatContext *s, inet_ntoa(rtsp_st->sdp_ip), rtsp_st->sdp_port, rtsp_st->sdp_ttl); - if (url_open(&rtsp_st->rtp_handle, url, URL_RDONLY) < 0) { + if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { err = AVERROR_INVALIDDATA; goto fail; } @@ -1320,10 +1381,15 @@ static int sdp_read_header(AVFormatContext *s, st = s->streams[rtsp_st->stream_index]; if (!st) s->ctx_flags |= AVFMTCTX_NOHEADER; - rtsp_st->rtp_ctx = rtp_parse_open(s, st, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data); + rtsp_st->rtp_ctx = rtp_parse_open(s, st, rtsp_st->rtp_handle, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data); if (!rtsp_st->rtp_ctx) { err = AVERROR_NOMEM; goto fail; + } else { + if(rtsp_st->dynamic_handler) { + rtsp_st->rtp_ctx->dynamic_protocol_context= rtsp_st->dynamic_protocol_context; + rtsp_st->rtp_ctx->parse_packet= rtsp_st->dynamic_handler->parse_packet; + } } } return 0; @@ -1345,8 +1411,8 @@ static int sdp_read_close(AVFormatContext *s) return 0; } - -static AVInputFormat sdp_demux = { +#ifdef CONFIG_SDP_DEMUXER +AVInputFormat sdp_demuxer = { "sdp", "SDP", sizeof(RTSPState), @@ -1355,8 +1421,9 @@ static AVInputFormat sdp_demux = { sdp_read_packet, sdp_read_close, }; +#endif - +#ifdef CONFIG_REDIR_DEMUXER /* dummy redirector format (used directly in av_open_input_file now) */ static int redir_probe(AVProbeData *pd) { @@ -1410,7 +1477,7 @@ int redir_open(AVFormatContext **ic_ptr, ByteIOContext *f) return 0; } -AVInputFormat redir_demux = { +AVInputFormat redir_demuxer = { "redir", "Redirector format", 0, @@ -1419,11 +1486,4 @@ AVInputFormat redir_demux = { NULL, NULL, }; - -int rtsp_init(void) -{ - av_register_input_format(&rtsp_demux); - av_register_input_format(&redir_demux); - av_register_input_format(&sdp_demux); - return 0; -} +#endif