From 93ced3e81a7672210b0252fd80e3a182e58e2d47 Mon Sep 17 00:00:00 2001 From: Fabrice Bellard Date: Mon, 18 Nov 2002 17:24:39 +0000 Subject: [PATCH] added multicast SDP/RTP demux for multicast streams - added support for MPEG4 video decoding in SDP/RTP Originally committed as revision 1223 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libav/rtsp.c | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 272 insertions(+), 15 deletions(-) diff --git a/libav/rtsp.c b/libav/rtsp.c index 81b59f8..ba87469 100644 --- a/libav/rtsp.c +++ b/libav/rtsp.c @@ -1,5 +1,5 @@ /* - * RTSP client + * RTSP/SDP client * Copyright (c) 2002 Fabrice Bellard. * * This library is free software; you can redistribute it and/or @@ -21,6 +21,7 @@ #include #include #include +#include #ifndef __BEOS__ # include #else @@ -41,7 +42,12 @@ typedef struct RTSPState { typedef struct RTSPStream { AVFormatContext *ic; int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */ - char control_url[1024]; /* url for this stream */ + char control_url[1024]; /* url for this stream (from SDP) */ + + int sdp_port; /* port (from SDP content - not used in RTSP) */ + struct in_addr sdp_ip; /* IP address (from SDP content - not used in RTSP) */ + int sdp_ttl; /* IP TTL (from SDP content - not used in RTSP) */ + int sdp_payload_type; /* payload type - only used in SDP */ } RTSPStream; /* suppress this hack */ @@ -115,21 +121,139 @@ static void get_word(char *buf, int buf_size, const char **pp) *pp = p; } -static void sdp_parse_line(AVFormatContext *s, +/* parse the rtpmap description: /[/] */ +static int sdp_parse_rtpmap(AVCodecContext *codec, const char *p) +{ + char buf[256]; + + /* codec name */ + get_word_sep(buf, sizeof(buf), "/", &p); + if (!strcmp(buf, "MP4V-ES")) { + codec->codec_id = CODEC_ID_MPEG4; + return 0; + } else { + return -1; + } +} + +/* return the length and optionnaly the data */ +static int hex_to_data(uint8_t *data, const char *p) +{ + int c, len, v; + + len = 0; + v = 1; + for(;;) { + skip_spaces(&p); + if (p == '\0') + break; + c = toupper((unsigned char)*p++); + if (c >= '0' && c <= '9') + c = c - '0'; + else if (c >= 'A' && c <= 'F') + c = c - 'A' + 10; + else + break; + v = (v << 4) | c; + if (v & 0x100) { + if (data) + data[len] = v; + len++; + v = 1; + } + } + return len; +} + +static void sdp_parse_fmtp(AVCodecContext *codec, const char *p) +{ + char attr[256]; + char value[4096]; + int len; + + /* 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++; + /* handle MPEG4 video */ + switch(codec->codec_id) { + case CODEC_ID_MPEG4: + if (!strcmp(attr, "config")) { + /* decode the hexa encoded parameter */ + len = hex_to_data(NULL, value); + codec->extradata = av_mallocz(len); + if (!codec->extradata) + goto fail; + codec->extradata_size = len; + hex_to_data(codec->extradata, value); + } + break; + default: + /* ignore data for other codecs */ + break; + } + fail: ; + // printf("'%s' = '%s'\n", attr, value); + } +} + +typedef struct SDPParseState { + /* SDP only */ + struct in_addr default_ip; + int default_ttl; +} SDPParseState; + +static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, int letter, const char *buf) { char buf1[64], st_type[64]; const char *p; - int codec_type, payload_type; + int codec_type, payload_type, i; AVStream *st; RTSPStream *rtsp_st; - + struct in_addr sdp_ip; + int ttl; + #ifdef DEBUG printf("sdp: %c='%s'\n", letter, buf); #endif p = buf; switch(letter) { + case 'c': + get_word(buf1, sizeof(buf1), &p); + if (strcmp(buf1, "IN") != 0) + return; + get_word(buf1, sizeof(buf1), &p); + if (strcmp(buf1, "IP4") != 0) + return; + get_word_sep(buf1, sizeof(buf1), "/", &p); + if (inet_aton(buf1, &sdp_ip) == 0) + return; + ttl = 16; + if (*p == '/') { + p++; + get_word_sep(buf1, sizeof(buf1), "/", &p); + ttl = atoi(buf1); + } + if (s->nb_streams == 0) { + s1->default_ip = sdp_ip; + s1->default_ttl = ttl; + } else { + st = s->streams[s->nb_streams - 1]; + rtsp_st = st->priv_data; + rtsp_st->sdp_ip = sdp_ip; + rtsp_st->sdp_ttl = ttl; + } + break; case 's': pstrcpy(s->title, sizeof(s->title), p); break; @@ -149,12 +273,6 @@ static void sdp_parse_line(AVFormatContext *s, } 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; @@ -162,10 +280,27 @@ static void sdp_parse_line(AVFormatContext *s, if (!st) return; st->priv_data = rtsp_st; + + rtsp_st->sdp_ip = s1->default_ip; + rtsp_st->sdp_ttl = s1->default_ttl; + + st->codec.codec_type = codec_type; + + get_word(buf1, sizeof(buf1), &p); /* port */ + rtsp_st->sdp_port = atoi(buf1); + + get_word(buf1, sizeof(buf1), &p); /* protocol (ignored) */ + + /* XXX: handle list of formats */ + get_word(buf1, sizeof(buf1), &p); /* format list */ + rtsp_st->sdp_payload_type = atoi(buf1); + if (rtsp_st->sdp_payload_type < 96) { + /* if standard payload type, we can find the codec right now */ + rtp_get_codec_info(&st->codec, rtsp_st->sdp_payload_type); + } + /* 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) { @@ -183,6 +318,28 @@ static void sdp_parse_line(AVFormatContext *s, } else { pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), p); } + } else if (strstart(p, "rtpmap:", &p)) { + /* NOTE: rtpmap is only supported AFTER the 'm=' tag */ + 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) { + sdp_parse_rtpmap(&st->codec, p); + } + } + } else if (strstart(p, "fmtp:", &p)) { + /* NOTE: fmtp is only supported AFTER the 'a=rtpmap:xxx' tag */ + 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) { + sdp_parse_fmtp(&st->codec, p); + } + } } break; } @@ -193,7 +350,9 @@ int sdp_parse(AVFormatContext *s, const char *content) const char *p; int letter; char buf[1024], *q; - + SDPParseState sdp_parse_state, *s1 = &sdp_parse_state; + + memset(s1, 0, sizeof(SDPParseState)); p = content; for(;;) { skip_spaces(&p); @@ -212,7 +371,7 @@ int sdp_parse(AVFormatContext *s, const char *content) p++; } *q = '\0'; - sdp_parse_line(s, letter, buf); + sdp_parse_line(s, s1, letter, buf); next_line: while (*p != '\n' && *p != '\0') p++; @@ -825,6 +984,103 @@ static AVInputFormat rtsp_demux = { .flags = AVFMT_NOFILE, }; + +/* XXX: add mime type support */ +static int sdp_probe(AVProbeData *p) +{ + if (match_ext(p->filename, "sdp")) + return AVPROBE_SCORE_MAX; + return 0; +} + +#define SDP_MAX_SIZE 8192 + +static int sdp_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + AVStream *st; + RTSPStream *rtsp_st; + int size, i, err; + char *content; + char url[1024]; + + /* read the whole sdp file */ + /* XXX: better loading */ + content = av_malloc(SDP_MAX_SIZE); + size = get_buffer(&s->pb, content, SDP_MAX_SIZE - 1); + if (size <= 0) { + av_free(content); + return AVERROR_INVALIDDATA; + } + content[size] ='\0'; + + sdp_parse(s, content); + av_free(content); + + /* open each RTP stream */ + for(i=0;inb_streams;i++) { + st = s->streams[i]; + rtsp_st = st->priv_data; + + snprintf(url, sizeof(url), "rtp://%s:%d?multicast=1&ttl=%d", + inet_ntoa(rtsp_st->sdp_ip), + rtsp_st->sdp_port, + rtsp_st->sdp_ttl); + if (av_open_input_file(&rtsp_st->ic, url, &rtp_demux, 0, NULL) < 0) { + err = AVERROR_INVALIDDATA; + 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); + } + return err; +} + +static int sdp_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + return udp_read_packet(s, pkt); +} + +static int sdp_read_close(AVFormatContext *s) +{ + AVStream *st; + RTSPStream *rtsp_st; + int i; + + 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); + } + return 0; +} + + +static AVInputFormat sdp_demux = { + "sdp", + "SDP", + sizeof(RTSPState), + sdp_probe, + sdp_read_header, + sdp_read_packet, + sdp_read_close, +}; + + /* dummy redirector format (used directly in av_open_input_file now) */ static int redir_probe(AVProbeData *pd) { @@ -892,5 +1148,6 @@ int rtsp_init(void) { av_register_input_format(&rtsp_demux); av_register_input_format(&redir_demux); + av_register_input_format(&sdp_demux); return 0; } -- 2.7.4