From 1617ad97aed7dd6b581c0a2e72693466d86f891a Mon Sep 17 00:00:00 2001 From: Fabrice Bellard Date: Thu, 4 Jul 2002 10:41:34 +0000 Subject: [PATCH] added rtsp support (not activated yet) Originally committed as revision 719 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libav/rtsp.c | 873 ++++++++++++++++++++++++++++++++++++++++++++++ libav/rtsp.h | 84 +++++ libav/rtspcodes.h | 11 + 3 files changed, 968 insertions(+) create mode 100644 libav/rtsp.c create mode 100644 libav/rtsp.h create mode 100644 libav/rtspcodes.h diff --git a/libav/rtsp.c b/libav/rtsp.c new file mode 100644 index 0000000000..8fa813916a --- /dev/null +++ b/libav/rtsp.c @@ -0,0 +1,873 @@ +/* + * RTSP client + * Copyright (c) 2002 Fabrice Bellard. + * + * This library 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. + * + * This library 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "avformat.h" + +#include +#include +#include +#include + +//#define DEBUG + +typedef struct RTSPState { + URLContext *rtsp_hd; /* RTSP TCP connexion handle */ + ByteIOContext rtsp_gb; + int seq; /* RTSP command sequence number */ + char session_id[512]; + enum RTSPProtocol protocol; + char last_reply[2048]; /* XXX: allocate ? */ +} RTSPState; + +typedef struct RTSPStream { + AVFormatContext *ic; + int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */ + char control_url[1024]; /* url for this stream */ +} RTSPStream; + +/* suppress this hack */ +int rtsp_abort_req = 0; + +/* XXX: currently, the only way to change the protocols consists in + changing this variable */ +int rtsp_default_protocols = (1 << RTSP_PROTOCOL_RTP_TCP) | (1 << RTSP_PROTOCOL_RTP_UDP) | (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST); + +FFRTSPCallback *ff_rtsp_callback = NULL; + +static int rtsp_probe(AVProbeData *p) +{ + if (strstart(p->filename, "rtsp:", NULL)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int redir_isspace(int c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); +} + +static void skip_spaces(const char **pp) +{ + const char *p; + p = *pp; + while (redir_isspace(*p)) + p++; + *pp = p; +} + +static void get_word_sep(char *buf, int buf_size, const char *sep, + const char **pp) +{ + const char *p; + char *q; + + p = *pp; + skip_spaces(&p); + q = buf; + while (!strchr(sep, *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) +{ + const char *p; + char *q; + + 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 sdp_parse_line(AVFormatContext *s, + int letter, const char *buf) +{ + char buf1[64], st_type[64]; + const char *p; + int codec_type, payload_type; + AVStream *st; + RTSPStream *rtsp_st; + +#ifdef DEBUG + printf("sdp: %c='%s'\n", letter, buf); +#endif + + p = buf; + switch(letter) { + case 's': + pstrcpy(s->title, sizeof(s->title), p); + break; + case 'i': + if (s->nb_streams == 0) { + pstrcpy(s->comment, sizeof(s->comment), p); + break; + } + break; + case 'm': + /* new stream */ + get_word(st_type, sizeof(st_type), &p); + if (!strcmp(st_type, "audio")) { + codec_type = CODEC_TYPE_AUDIO; + } else if (!strcmp(st_type, "video")) { + codec_type = CODEC_TYPE_VIDEO; + } else { + return; + } + get_word(buf1, sizeof(buf1), &p); /* port */ + get_word(buf1, sizeof(buf1), &p); /* protocol */ + /* XXX: handle list of formats */ + get_word(buf1, sizeof(buf1), &p); /* format list */ + payload_type = atoi(buf1); + + rtsp_st = av_mallocz(sizeof(RTSPStream)); + if (!rtsp_st) + return; + st = av_new_stream(s, s->nb_streams); + if (!st) + return; + st->priv_data = rtsp_st; + /* put a default control url */ + pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), s->filename); + st->codec.codec_type = codec_type; + rtp_get_codec_info(&st->codec, payload_type); + break; + case 'a': + if (strstart(p, "control:", &p) && s->nb_streams > 0) { + char proto[32]; + /* get the control url */ + st = s->streams[s->nb_streams - 1]; + rtsp_st = st->priv_data; + + /* XXX: may need to add full url resolution */ + url_split(proto, sizeof(proto), NULL, 0, NULL, NULL, 0, p); + if (proto[0] == '\0') { + /* relative control URL */ + pstrcat(rtsp_st->control_url, sizeof(rtsp_st->control_url), "/"); + pstrcat(rtsp_st->control_url, sizeof(rtsp_st->control_url), p); + } else { + pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), p); + } + } + break; + } +} + +int sdp_parse(AVFormatContext *s, const char *content) +{ + const char *p; + int letter; + char buf[1024], *q; + + p = content; + for(;;) { + skip_spaces(&p); + letter = *p; + if (letter == '\0') + break; + p++; + if (*p != '=') + goto next_line; + p++; + /* get the content */ + q = buf; + while (*p != '\n' && *p != '\0') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + sdp_parse_line(s, letter, buf); + next_line: + while (*p != '\n' && *p != '\0') + p++; + if (*p == '\n') + p++; + } + return 0; +} + +static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp) +{ + const char *p; + int v; + + p = *pp; + skip_spaces(&p); + v = strtol(p, (char **)&p, 10); + if (*p == '-') { + p++; + *min_ptr = v; + v = strtol(p, (char **)&p, 10); + *max_ptr = v; + } else { + *min_ptr = v; + *max_ptr = v; + } + *pp = p; +} + +/* XXX: only one transport specification is parsed */ +static void rtsp_parse_transport(RTSPHeader *reply, const char *p) +{ + char transport_protocol[16]; + char profile[16]; + char lower_transport[16]; + char parameter[16]; + RTSPTransportField *th; + char buf[256]; + + reply->nb_transports = 0; + + for(;;) { + skip_spaces(&p); + if (*p == '\0') + break; + + th = &reply->transports[reply->nb_transports]; + + get_word_sep(transport_protocol, sizeof(transport_protocol), + "/", &p); + if (*p == '/') + p++; + get_word_sep(profile, sizeof(profile), "/;,", &p); + lower_transport[0] = '\0'; + if (*p == '/') { + get_word_sep(lower_transport, sizeof(lower_transport), + ";,", &p); + } + if (!strcmp(lower_transport, "TCP")) + th->protocol = RTSP_PROTOCOL_RTP_TCP; + else + th->protocol = RTSP_PROTOCOL_RTP_UDP; + + if (*p == ';') + p++; + /* get each parameter */ + while (*p != '\0' && *p != ',') { + get_word_sep(parameter, sizeof(parameter), "=;,", &p); + if (!strcmp(parameter, "port")) { + if (*p == '=') { + p++; + rtsp_parse_range(&th->port_min, &th->port_max, &p); + } + } else if (!strcmp(parameter, "client_port")) { + if (*p == '=') { + p++; + rtsp_parse_range(&th->client_port_min, + &th->client_port_max, &p); + } + } else if (!strcmp(parameter, "server_port")) { + if (*p == '=') { + p++; + rtsp_parse_range(&th->server_port_min, + &th->server_port_max, &p); + } + } else if (!strcmp(parameter, "interleaved")) { + if (*p == '=') { + p++; + rtsp_parse_range(&th->interleaved_min, + &th->interleaved_max, &p); + } + } else if (!strcmp(parameter, "multicast")) { + if (th->protocol == RTSP_PROTOCOL_RTP_UDP) + th->protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST; + } else if (!strcmp(parameter, "ttl")) { + if (*p == '=') { + p++; + th->ttl = strtol(p, (char **)&p, 10); + } + } else if (!strcmp(parameter, "destination")) { + struct in_addr ipaddr; + + if (*p == '=') { + p++; + get_word_sep(buf, sizeof(buf), ";,", &p); + if (inet_aton(buf, &ipaddr)) + th->destination = ntohl(ipaddr.s_addr); + } + } + while (*p != ';' && *p != '\0' && *p != ',') + p++; + if (*p == ';') + p++; + } + if (*p == ',') + p++; + + reply->nb_transports++; + } +} + +void rtsp_parse_line(RTSPHeader *reply, const char *buf) +{ + const char *p; + + /* NOTE: we do case independent match for broken servers */ + p = buf; + if (stristart(p, "Session:", &p)) { + get_word_sep(reply->session_id, sizeof(reply->session_id), ";", &p); + } else if (stristart(p, "Content-Length:", &p)) { + reply->content_length = strtol(p, NULL, 10); + } else if (stristart(p, "Transport:", &p)) { + rtsp_parse_transport(reply, p); + } else if (stristart(p, "CSeq:", &p)) { + reply->seq = strtol(p, NULL, 10); + } +} + + +static void rtsp_send_cmd(AVFormatContext *s, + const char *cmd, RTSPHeader *reply, + unsigned char **content_ptr) +{ + RTSPState *rt = s->priv_data; + char buf[4096], buf1[1024], *q; + unsigned char ch; + const char *p; + int content_length, line_count; + unsigned char *content = NULL; + + memset(reply, 0, sizeof(RTSPHeader)); + + rt->seq++; + pstrcpy(buf, sizeof(buf), cmd); + snprintf(buf1, sizeof(buf1), "CSeq: %d\n", rt->seq); + pstrcat(buf, sizeof(buf), buf1); + if (rt->session_id[0] != '\0' && !strstr(cmd, "\nIf-Match:")) { + snprintf(buf1, sizeof(buf1), "Session: %s\n", rt->session_id); + pstrcat(buf, sizeof(buf), buf1); + } + pstrcat(buf, sizeof(buf), "\n"); +#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_read(rt->rtsp_hd, &ch, 1) == 0) + break; + if (ch == '\n') + break; + if (ch != '\r') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = ch; + } + } + *q = '\0'; +#ifdef DEBUG + printf("line='%s'\n", buf); +#endif + /* test if last line */ + if (buf[0] == '\0') + break; + p = buf; + if (line_count == 0) { + /* get reply code */ + get_word(buf1, sizeof(buf1), &p); + get_word(buf1, sizeof(buf1), &p); + reply->status_code = atoi(buf1); + } else { + rtsp_parse_line(reply, p); + pstrcat(rt->last_reply, sizeof(rt->last_reply), p); + pstrcat(rt->last_reply, sizeof(rt->last_reply), "\n"); + } + line_count++; + } + + if (rt->session_id[0] == '\0' && reply->session_id[0] != '\0') + pstrcpy(rt->session_id, sizeof(rt->session_id), reply->session_id); + + content_length = reply->content_length; + if (content_length > 0) { + /* leave some room for a trailing '\0' (useful for simple parsing) */ + content = av_malloc(content_length + 1); + url_read(rt->rtsp_hd, content, content_length); + content[content_length] = '\0'; + } + if (content_ptr) + *content_ptr = content; +} + +/* useful for modules: set RTSP callback function */ + +void rtsp_set_callback(FFRTSPCallback *rtsp_cb) +{ + ff_rtsp_callback = rtsp_cb; +} + + +static int rtsp_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + RTSPState *rt = s->priv_data; + char host[1024], path[1024], tcpname[1024], cmd[2048]; + URLContext *rtsp_hd; + int port, i, ret, err; + RTSPHeader reply1, *reply = &reply1; + unsigned char *content = NULL; + AVStream *st; + RTSPStream *rtsp_st; + int protocol_mask; + + rtsp_abort_req = 0; + + /* extract hostname and port */ + url_split(NULL, 0, + host, sizeof(host), &port, path, sizeof(path), s->filename); + if (port < 0) + port = RTSP_DEFAULT_PORT; + + /* open the tcp connexion */ + snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", host, port); + if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) + return AVERROR_IO; + rt->rtsp_hd = rtsp_hd; + rt->seq = 0; + + /* describe the stream */ + snprintf(cmd, sizeof(cmd), + "DESCRIBE %s RTSP/1.0\n" + "Accept: application/sdp\n", + s->filename); + rtsp_send_cmd(s, cmd, reply, &content); + if (!content) { + err = AVERROR_INVALIDDATA; + goto fail; + } + if (reply->status_code != RTSP_STATUS_OK) { + err = AVERROR_INVALIDDATA; + goto fail; + } + + /* now we got the SDP description, we parse it */ + ret = sdp_parse(s, (const char *)content); + av_freep(&content); + if (ret < 0) { + err = AVERROR_INVALIDDATA; + 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 */ + for(i=0;inb_streams;i++) { + AVInputFormat *fmt; + char transport[2048]; + + st = s->streams[i]; + rtsp_st = st->priv_data; + + /* compute available transports */ + transport[0] = '\0'; + + /* RTP/UDP */ + if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP)) { + fmt = &rtp_demux; + if (av_open_input_file(&rtsp_st->ic, "rtp://", fmt, 0, NULL) < 0) { + err = AVERROR_INVALIDDATA; + goto fail; + } + + port = rtp_get_local_port(url_fileno(&rtsp_st->ic->pb)); + if (transport[0] != '\0') + pstrcat(transport, sizeof(transport), ","); + snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1, + "RTP/AVP/UDP;unicast;client_port=%d-%d", + port, port + 1); + } + + /* RTP/TCP */ + if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_TCP)) { + if (transport[0] != '\0') + pstrcat(transport, sizeof(transport), ","); + snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1, + "RTP/AVP/TCP"); + } + + if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST)) { + if (transport[0] != '\0') + pstrcat(transport, sizeof(transport), ","); + snprintf(transport + strlen(transport), + sizeof(transport) - strlen(transport) - 1, + "RTP/AVP/UDP;multicast"); + } + + snprintf(cmd, sizeof(cmd), + "SETUP %s RTSP/1.0\n" + "Transport: %s\n", + rtsp_st->control_url, transport); + rtsp_send_cmd(s, cmd, reply, NULL); + if (reply->status_code != RTSP_STATUS_OK || + reply->nb_transports != 1) { + err = AVERROR_INVALIDDATA; + goto fail; + } + + /* XXX: same protocol for all streams is required */ + if (i > 0) { + if (reply->transports[0].protocol != rt->protocol) { + err = AVERROR_INVALIDDATA; + goto fail; + } + } else { + rt->protocol = reply->transports[0].protocol; + } + + /* close RTP connection if not choosen */ + if (reply->transports[0].protocol != RTSP_PROTOCOL_RTP_UDP && + (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP))) { + av_close_input_file(rtsp_st->ic); + rtsp_st->ic = NULL; + } + + switch(reply->transports[0].protocol) { + case RTSP_PROTOCOL_RTP_TCP: + fmt = &rtp_demux; + if (av_open_input_file(&rtsp_st->ic, "null", fmt, 0, NULL) < 0) { + err = AVERROR_INVALIDDATA; + goto fail; + } + rtsp_st->interleaved_min = reply->transports[0].interleaved_min; + rtsp_st->interleaved_max = reply->transports[0].interleaved_max; + break; + + case RTSP_PROTOCOL_RTP_UDP: + { + char url[1024]; + + /* XXX: also use address if specified */ + snprintf(url, sizeof(url), "rtp://%s:%d", + host, reply->transports[0].server_port_min); + if (rtp_set_remote_url(url_fileno(&rtsp_st->ic->pb), url) < 0) { + err = AVERROR_INVALIDDATA; + goto fail; + } + } + break; + case RTSP_PROTOCOL_RTP_UDP_MULTICAST: + { + char url[1024]; + int ttl; + + fmt = &rtp_demux; + ttl = reply->transports[0].ttl; + if (!ttl) + ttl = 16; + snprintf(url, sizeof(url), "rtp://%s:%d?multicast=1&ttl=%d", + host, + reply->transports[0].server_port_min, + ttl); + if (av_open_input_file(&rtsp_st->ic, url, fmt, 0, NULL) < 0) { + err = AVERROR_INVALIDDATA; + goto fail; + } + } + break; + } + } + + /* 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; + } + } + + /* start playing */ + snprintf(cmd, sizeof(cmd), + "PLAY %s RTSP/1.0\n", + s->filename); + rtsp_send_cmd(s, cmd, reply, NULL); + if (reply->status_code != RTSP_STATUS_OK) { + err = AVERROR_INVALIDDATA; + goto fail; + } + + /* open TCP with bufferized input */ + if (rt->protocol == RTSP_PROTOCOL_RTP_TCP) { + if (url_fdopen(&rt->rtsp_gb, rt->rtsp_hd) < 0) { + err = AVERROR_NOMEM; + goto fail; + } + } + + return 0; + fail: + for(i=0;inb_streams;i++) { + st = s->streams[i]; + rtsp_st = st->priv_data; + if (rtsp_st) { + if (rtsp_st->ic) + av_close_input_file(rtsp_st->ic); + } + av_free(rtsp_st); + } + av_freep(&content); + url_close(rt->rtsp_hd); + return err; +} + +static int tcp_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + RTSPState *rt = s->priv_data; + ByteIOContext *rtsp_gb = &rt->rtsp_gb; + int c, id, len, i, ret; + AVStream *st; + RTSPStream *rtsp_st; + char buf[RTP_MAX_PACKET_LENGTH]; + + redo: + for(;;) { + c = url_fgetc(rtsp_gb); + if (c == URL_EOF) + return AVERROR_IO; + if (c == '$') + break; + } + id = get_byte(rtsp_gb); + len = get_be16(rtsp_gb); + if (len > RTP_MAX_PACKET_LENGTH || len < 12) + goto redo; + /* get the data */ + get_buffer(rtsp_gb, buf, len); + + /* find the matching stream */ + for(i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + rtsp_st = st->priv_data; + if (i >= rtsp_st->interleaved_min && + i <= rtsp_st->interleaved_max) + goto found; + } + goto redo; + found: + ret = rtp_parse_packet(rtsp_st->ic, pkt, buf, len); + if (ret < 0) + goto redo; + pkt->stream_index = i; + return ret; +} + +/* NOTE: output one packet at a time. May need to add a small fifo */ +static int udp_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + AVFormatContext *ic; + AVStream *st; + RTSPStream *rtsp_st; + fd_set rfds; + int fd1, fd2, fd_max, n, i, ret; + char buf[RTP_MAX_PACKET_LENGTH]; + struct timeval tv; + + for(;;) { + if (rtsp_abort_req) + return -EIO; + FD_ZERO(&rfds); + fd_max = -1; + for(i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + rtsp_st = st->priv_data; + ic = rtsp_st->ic; + /* currently, we cannot probe RTCP handle because of blocking restrictions */ + rtp_get_file_handles(url_fileno(&ic->pb), &fd1, &fd2); + if (fd1 > fd_max) + fd_max = fd1; + FD_SET(fd1, &rfds); + } + /* XXX: also add proper API to abort */ + tv.tv_sec = 0; + tv.tv_usec = 500000; + n = select(fd_max + 1, &rfds, NULL, NULL, &tv); + if (n > 0) { + for(i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + rtsp_st = st->priv_data; + ic = rtsp_st->ic; + rtp_get_file_handles(url_fileno(&ic->pb), &fd1, &fd2); + if (FD_ISSET(fd1, &rfds)) { + ret = url_read(url_fileno(&ic->pb), buf, sizeof(buf)); + if (ret >= 0 && + rtp_parse_packet(ic, pkt, buf, ret) == 0) { + pkt->stream_index = i; + return ret; + } + } + } + } + } +} + +static int rtsp_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + RTSPState *rt = s->priv_data; + int ret; + + switch(rt->protocol) { + default: + case RTSP_PROTOCOL_RTP_TCP: + ret = tcp_read_packet(s, pkt); + break; + case RTSP_PROTOCOL_RTP_UDP: + ret = udp_read_packet(s, pkt); + break; + } + return ret; +} + +static int rtsp_read_close(AVFormatContext *s) +{ + RTSPState *rt = s->priv_data; + AVStream *st; + RTSPStream *rtsp_st; + RTSPHeader reply1, *reply = &reply1; + int i; + char cmd[1024]; + + /* NOTE: it is valid to flush the buffer here */ + if (rt->protocol == RTSP_PROTOCOL_RTP_TCP) { + url_fclose(&rt->rtsp_gb); + } + + snprintf(cmd, sizeof(cmd), + "TEARDOWN %s RTSP/1.0\n", + 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); + } + + for(i=0;inb_streams;i++) { + st = s->streams[i]; + rtsp_st = st->priv_data; + if (rtsp_st) { + if (rtsp_st->ic) + av_close_input_file(rtsp_st->ic); + } + av_free(rtsp_st); + } + url_close(rt->rtsp_hd); + return 0; +} + +static AVInputFormat rtsp_demux = { + "rtsp", + "RTSP input format", + sizeof(RTSPState), + rtsp_probe, + rtsp_read_header, + rtsp_read_packet, + rtsp_read_close, + flags: AVFMT_NOFILE, +}; + +/* dummy redirector format (used directly in av_open_input_file now) */ +static int redir_probe(AVProbeData *pd) +{ + const char *p; + p = pd->buf; + while (redir_isspace(*p)) + p++; + if (strstart(p, "http://", NULL) || + strstart(p, "rtsp://", NULL)) + return AVPROBE_SCORE_MAX; + return 0; +} + +/* called from utils.c */ +int redir_open(AVFormatContext **ic_ptr, ByteIOContext *f) +{ + char buf[4096], *q; + int c; + AVFormatContext *ic = NULL; + + /* parse each URL and try to open it */ + c = url_fgetc(f); + while (c != URL_EOF) { + /* skip spaces */ + for(;;) { + if (!redir_isspace(c)) + break; + c = url_fgetc(f); + } + if (c == URL_EOF) + break; + /* record url */ + q = buf; + for(;;) { + if (c == URL_EOF || redir_isspace(c)) + break; + if ((q - buf) < sizeof(buf) - 1) + *q++ = c; + c = url_fgetc(f); + } + *q = '\0'; + //printf("URL='%s'\n", buf); + /* try to open the media file */ + if (av_open_input_file(&ic, buf, NULL, 0, NULL) == 0) + break; + } + *ic_ptr = ic; + if (!ic) + return AVERROR_IO; + else + return 0; +} + +AVInputFormat redir_demux = { + "redir", + "Redirector format", + 0, + redir_probe, + NULL, + NULL, + NULL, +}; + +int rtsp_init(void) +{ + av_register_input_format(&rtsp_demux); + av_register_input_format(&redir_demux); + return 0; +} diff --git a/libav/rtsp.h b/libav/rtsp.h new file mode 100644 index 0000000000..f683bf338b --- /dev/null +++ b/libav/rtsp.h @@ -0,0 +1,84 @@ +/* + * RTSP definitions + * Copyright (c) 2002 Fabrice Bellard. + * + * This library 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. + * + * This library 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef RTSP_H +#define RTSP_H + +/* RTSP handling */ +enum RTSPStatusCode { +#define DEF(n, c, s) c = n, +#include "rtspcodes.h" +#undef DEF +}; + +enum RTSPProtocol { + RTSP_PROTOCOL_RTP_UDP = 0, + RTSP_PROTOCOL_RTP_TCP = 1, + RTSP_PROTOCOL_RTP_UDP_MULTICAST = 2, +}; + +#define RTSP_DEFAULT_PORT 554 +#define RTSP_MAX_TRANSPORTS 8 + +typedef struct RTSPTransportField { + int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */ + int port_min, port_max; /* RTP ports */ + int client_port_min, client_port_max; /* RTP ports */ + int server_port_min, server_port_max; /* RTP ports */ + int ttl; /* ttl value */ + UINT32 destination; /* destination IP address */ + enum RTSPProtocol protocol; +} RTSPTransportField; + +typedef struct RTSPHeader { + int content_length; + enum RTSPStatusCode status_code; /* response code from server */ + int nb_transports; + RTSPTransportField transports[RTSP_MAX_TRANSPORTS]; + int seq; /* sequence number */ + char session_id[512]; +} RTSPHeader; + +/* the callback can be used to extend the connection setup/teardown step */ +enum RTSPCallbackAction { + RTSP_ACTION_SERVER_SETUP, + RTSP_ACTION_SERVER_TEARDOWN, + RTSP_ACTION_CLIENT_SETUP, + RTSP_ACTION_CLIENT_TEARDOWN, +}; + +typedef struct RTSPActionServerSetup { + UINT32 ipaddr; + char transport_option[512]; +} RTSPActionServerSetup; + +typedef int FFRTSPCallback(enum RTSPCallbackAction action, + const char *session_id, + char *buf, int buf_size, + void *arg); + +void rtsp_set_callback(FFRTSPCallback *rtsp_cb); + +int rtsp_init(void); +void rtsp_parse_line(RTSPHeader *reply, const char *buf); + +extern int rtsp_abort_req; +extern int rtsp_default_protocols; +extern FFRTSPCallback *ff_rtsp_callback; + +#endif /* RTSP_H */ diff --git a/libav/rtspcodes.h b/libav/rtspcodes.h new file mode 100644 index 0000000000..b967cb932a --- /dev/null +++ b/libav/rtspcodes.h @@ -0,0 +1,11 @@ +DEF(200, RTSP_STATUS_OK, "OK") +DEF(405, RTSP_STATUS_METHOD, "Method Not Allowed") +DEF(453, RTSP_STATUS_BANDWIDTH, "Not Enough Bandwidth") +DEF(454, RTSP_STATUS_SESSION, "Session Not Found") +DEF(455, RTSP_STATUS_STATE, "Method Not Valid in This State") +DEF(459, RTSP_STATUS_AGGREGATE, "Aggregate operation not allowed") +DEF(460, RTSP_STATUS_ONLY_AGGREGATE, "Only aggregate operation allowed") +DEF(461, RTSP_STATUS_TRANSPORT, "Unsupported transport") +DEF(500, RTSP_STATUS_INTERNAL, "Internal Server Error") +DEF(503, RTSP_STATUS_SERVICE, "Service Unavailable") +DEF(505, RTSP_STATUS_VERSION, "RTSP Version not supported")