mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2024-09-19 21:06:42 +00:00
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
This commit is contained in:
parent
5b25dfa708
commit
93ced3e81a
283
libav/rtsp.c
283
libav/rtsp.c
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* RTSP client
|
* RTSP/SDP client
|
||||||
* Copyright (c) 2002 Fabrice Bellard.
|
* Copyright (c) 2002 Fabrice Bellard.
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
@ -21,6 +21,7 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <ctype.h>
|
||||||
#ifndef __BEOS__
|
#ifndef __BEOS__
|
||||||
# include <arpa/inet.h>
|
# include <arpa/inet.h>
|
||||||
#else
|
#else
|
||||||
@ -41,7 +42,12 @@ typedef struct RTSPState {
|
|||||||
typedef struct RTSPStream {
|
typedef struct RTSPStream {
|
||||||
AVFormatContext *ic;
|
AVFormatContext *ic;
|
||||||
int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */
|
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;
|
} RTSPStream;
|
||||||
|
|
||||||
/* suppress this hack */
|
/* suppress this hack */
|
||||||
@ -115,14 +121,106 @@ static void get_word(char *buf, int buf_size, const char **pp)
|
|||||||
*pp = p;
|
*pp = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdp_parse_line(AVFormatContext *s,
|
/* parse the rtpmap description: <codec_name>/<clock_rate>[/<other
|
||||||
|
params>] */
|
||||||
|
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)
|
int letter, const char *buf)
|
||||||
{
|
{
|
||||||
char buf1[64], st_type[64];
|
char buf1[64], st_type[64];
|
||||||
const char *p;
|
const char *p;
|
||||||
int codec_type, payload_type;
|
int codec_type, payload_type, i;
|
||||||
AVStream *st;
|
AVStream *st;
|
||||||
RTSPStream *rtsp_st;
|
RTSPStream *rtsp_st;
|
||||||
|
struct in_addr sdp_ip;
|
||||||
|
int ttl;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("sdp: %c='%s'\n", letter, buf);
|
printf("sdp: %c='%s'\n", letter, buf);
|
||||||
@ -130,6 +228,32 @@ static void sdp_parse_line(AVFormatContext *s,
|
|||||||
|
|
||||||
p = buf;
|
p = buf;
|
||||||
switch(letter) {
|
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':
|
case 's':
|
||||||
pstrcpy(s->title, sizeof(s->title), p);
|
pstrcpy(s->title, sizeof(s->title), p);
|
||||||
break;
|
break;
|
||||||
@ -149,12 +273,6 @@ static void sdp_parse_line(AVFormatContext *s,
|
|||||||
} else {
|
} else {
|
||||||
return;
|
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));
|
rtsp_st = av_mallocz(sizeof(RTSPStream));
|
||||||
if (!rtsp_st)
|
if (!rtsp_st)
|
||||||
return;
|
return;
|
||||||
@ -162,10 +280,27 @@ static void sdp_parse_line(AVFormatContext *s,
|
|||||||
if (!st)
|
if (!st)
|
||||||
return;
|
return;
|
||||||
st->priv_data = rtsp_st;
|
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 */
|
/* put a default control url */
|
||||||
pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), s->filename);
|
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;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
if (strstart(p, "control:", &p) && s->nb_streams > 0) {
|
if (strstart(p, "control:", &p) && s->nb_streams > 0) {
|
||||||
@ -183,6 +318,28 @@ static void sdp_parse_line(AVFormatContext *s,
|
|||||||
} else {
|
} else {
|
||||||
pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), p);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@ -193,7 +350,9 @@ int sdp_parse(AVFormatContext *s, const char *content)
|
|||||||
const char *p;
|
const char *p;
|
||||||
int letter;
|
int letter;
|
||||||
char buf[1024], *q;
|
char buf[1024], *q;
|
||||||
|
SDPParseState sdp_parse_state, *s1 = &sdp_parse_state;
|
||||||
|
|
||||||
|
memset(s1, 0, sizeof(SDPParseState));
|
||||||
p = content;
|
p = content;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
skip_spaces(&p);
|
skip_spaces(&p);
|
||||||
@ -212,7 +371,7 @@ int sdp_parse(AVFormatContext *s, const char *content)
|
|||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
*q = '\0';
|
*q = '\0';
|
||||||
sdp_parse_line(s, letter, buf);
|
sdp_parse_line(s, s1, letter, buf);
|
||||||
next_line:
|
next_line:
|
||||||
while (*p != '\n' && *p != '\0')
|
while (*p != '\n' && *p != '\0')
|
||||||
p++;
|
p++;
|
||||||
@ -825,6 +984,103 @@ static AVInputFormat rtsp_demux = {
|
|||||||
.flags = AVFMT_NOFILE,
|
.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;i<s->nb_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;i<s->nb_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;i<s->nb_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) */
|
/* dummy redirector format (used directly in av_open_input_file now) */
|
||||||
static int redir_probe(AVProbeData *pd)
|
static int redir_probe(AVProbeData *pd)
|
||||||
{
|
{
|
||||||
@ -892,5 +1148,6 @@ int rtsp_init(void)
|
|||||||
{
|
{
|
||||||
av_register_input_format(&rtsp_demux);
|
av_register_input_format(&rtsp_demux);
|
||||||
av_register_input_format(&redir_demux);
|
av_register_input_format(&redir_demux);
|
||||||
|
av_register_input_format(&sdp_demux);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user