RTP/RTSP and MPEG4-AAC audio

- preliminary support for mpeg4-aac rtp payload (no interleaving support)
  - use udp transport as default (makes more sense with rtp, doesn't it ?)
  - some code factorization, so adding support for new rtp payload will be easier
  (I hope ;-)
patch by (Romain DEGEZ: romain degez, smartjog com)

Originally committed as revision 4306 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
Romain Degez 2005-05-26 07:47:51 +00:00 committed by Michael Niedermayer
parent 3072f0cb2e
commit d1ccf0e0a6
4 changed files with 462 additions and 181 deletions

View File

@ -18,6 +18,7 @@
*/
#include "avformat.h"
#include "mpegts.h"
#include "bitstream.h"
#include <unistd.h>
#include <sys/types.h>
@ -42,36 +43,146 @@
'url_open_dyn_packet_buf')
*/
#define RTP_VERSION 2
/* from http://www.iana.org/assignments/rtp-parameters last updated 05 January 2005 */
AVRtpPayloadType_t AVRtpPayloadTypes[]=
{
{0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8000, 1},
{1, "Reserved", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{2, "Reserved", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1},
{4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1},
{5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1},
{6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1},
{7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1},
{8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1},
{9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1},
{10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2},
{11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1},
{12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1},
{13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1},
{14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, 90000, -1},
{15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1},
{16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1},
{17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1},
{18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1},
{19, "reserved", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1},
{20, "unassigned", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1},
{21, "unassigned", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1},
{22, "unassigned", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1},
{23, "unassigned", CODEC_TYPE_AUDIO, CODEC_ID_NONE, -1, -1},
{24, "unassigned", CODEC_TYPE_VIDEO, CODEC_ID_NONE, -1, -1},
{25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1},
{26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1},
{27, "unassigned", CODEC_TYPE_VIDEO, CODEC_ID_NONE, -1, -1},
{28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1},
{29, "unassigned", CODEC_TYPE_VIDEO, CODEC_ID_NONE, -1, -1},
{30, "unassigned", CODEC_TYPE_VIDEO, CODEC_ID_NONE, -1, -1},
{31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1},
{32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1},
{33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1},
{34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1},
{35, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{36, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{37, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{38, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{39, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{40, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{41, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{42, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{43, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{44, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{45, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{46, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{47, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{48, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{49, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{50, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{51, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{52, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{53, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{54, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{55, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{56, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{57, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{58, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{59, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{60, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{61, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{62, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{63, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{64, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{65, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{66, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{67, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{68, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{69, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{70, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{71, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{72, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{73, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{74, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{75, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{76, "reserved for RTCP conflict avoidance", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{77, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{78, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{79, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{80, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{81, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{82, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{83, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{84, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{85, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{86, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{87, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{88, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{89, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{90, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{91, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{92, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{93, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{94, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{95, "unassigned", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{96, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{97, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{98, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{99, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{100, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{101, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{102, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{103, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{104, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{105, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{106, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{107, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{108, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{109, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{110, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{111, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{112, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{113, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{114, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{115, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{116, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{117, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{118, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{119, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{120, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{121, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{122, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{123, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{124, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{125, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{126, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{127, "dynamic", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1},
{-1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1}
};
#define RTP_MAX_SDES 256 /* maximum text length for SDES */
/* RTCP paquets use 0.5 % of the bandwidth */
#define RTCP_TX_RATIO_NUM 5
#define RTCP_TX_RATIO_DEN 1000
typedef enum {
RTCP_SR = 200,
RTCP_RR = 201,
RTCP_SDES = 202,
RTCP_BYE = 203,
RTCP_APP = 204
} rtcp_type_t;
typedef enum {
RTCP_SDES_END = 0,
RTCP_SDES_CNAME = 1,
RTCP_SDES_NAME = 2,
RTCP_SDES_EMAIL = 3,
RTCP_SDES_PHONE = 4,
RTCP_SDES_LOC = 5,
RTCP_SDES_TOOL = 6,
RTCP_SDES_NOTE = 7,
RTCP_SDES_PRIV = 8,
RTCP_SDES_IMG = 9,
RTCP_SDES_DOOR = 10,
RTCP_SDES_SOURCE = 11
} rtcp_sdes_type_t;
AVRtpDynamicPayloadType_t AVRtpDynamicPayloadTypes[]=
{
{"MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4},
{"mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_MPEG4AAC},
{"", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE}
};
struct RTPDemuxContext {
AVFormatContext *ic;
@ -83,7 +194,7 @@ struct RTPDemuxContext {
uint32_t base_timestamp;
uint32_t cur_timestamp;
int max_payload_size;
MpegTSContext *ts; /* only used for RTP_PT_MPEG2TS payloads */
MpegTSContext *ts; /* only used for MP2T payloads */
int read_buf_index;
int read_buf_size;
@ -99,94 +210,37 @@ struct RTPDemuxContext {
/* buffer for output */
uint8_t buf[RTP_MAX_PACKET_LENGTH];
uint8_t *buf_ptr;
/* special infos for au headers parsing */
rtp_payload_data_t *rtp_payload_data;
};
int rtp_get_codec_info(AVCodecContext *codec, int payload_type)
{
switch(payload_type) {
case RTP_PT_ULAW:
codec->codec_type = CODEC_TYPE_AUDIO;
codec->codec_id = CODEC_ID_PCM_MULAW;
codec->channels = 1;
codec->sample_rate = 8000;
break;
case RTP_PT_ALAW:
codec->codec_type = CODEC_TYPE_AUDIO;
codec->codec_id = CODEC_ID_PCM_ALAW;
codec->channels = 1;
codec->sample_rate = 8000;
break;
case RTP_PT_S16BE_STEREO:
codec->codec_type = CODEC_TYPE_AUDIO;
codec->codec_id = CODEC_ID_PCM_S16BE;
codec->channels = 2;
codec->sample_rate = 44100;
break;
case RTP_PT_S16BE_MONO:
codec->codec_type = CODEC_TYPE_AUDIO;
codec->codec_id = CODEC_ID_PCM_S16BE;
codec->channels = 1;
codec->sample_rate = 44100;
break;
case RTP_PT_MPEGAUDIO:
codec->codec_type = CODEC_TYPE_AUDIO;
codec->codec_id = CODEC_ID_MP2;
break;
case RTP_PT_JPEG:
codec->codec_type = CODEC_TYPE_VIDEO;
codec->codec_id = CODEC_ID_MJPEG;
break;
case RTP_PT_MPEGVIDEO:
codec->codec_type = CODEC_TYPE_VIDEO;
codec->codec_id = CODEC_ID_MPEG1VIDEO;
break;
case RTP_PT_MPEG2TS:
codec->codec_type = CODEC_TYPE_DATA;
codec->codec_id = CODEC_ID_MPEG2TS;
break;
default:
return -1;
if (AVRtpPayloadTypes[payload_type].codec_id != CODEC_ID_NONE) {
codec->codec_type = AVRtpPayloadTypes[payload_type].codec_type;
codec->codec_id = AVRtpPayloadTypes[payload_type].codec_type;
if (AVRtpPayloadTypes[payload_type].audio_channels > 0)
codec->channels = AVRtpPayloadTypes[payload_type].audio_channels;
if (AVRtpPayloadTypes[payload_type].clock_rate > 0)
codec->sample_rate = AVRtpPayloadTypes[payload_type].clock_rate;
return 0;
}
return 0;
return -1;
}
/* return < 0 if unknown payload type */
int rtp_get_payload_type(AVCodecContext *codec)
{
int payload_type;
int i, payload_type;
/* compute the payload type */
payload_type = -1;
switch(codec->codec_id) {
case CODEC_ID_PCM_MULAW:
payload_type = RTP_PT_ULAW;
break;
case CODEC_ID_PCM_ALAW:
payload_type = RTP_PT_ALAW;
break;
case CODEC_ID_PCM_S16BE:
if (codec->channels == 1) {
payload_type = RTP_PT_S16BE_MONO;
} else if (codec->channels == 2) {
payload_type = RTP_PT_S16BE_STEREO;
for (payload_type = -1, i = 0; AVRtpPayloadTypes[i].pt >= 0; ++i)
if (AVRtpPayloadTypes[i].codec_id == codec->codec_id) {
if (codec->codec_id == CODEC_ID_PCM_S16BE)
if (codec->channels != AVRtpPayloadTypes[i].audio_channels)
continue;
payload_type = AVRtpPayloadTypes[i].pt;
}
break;
case CODEC_ID_MP2:
case CODEC_ID_MP3:
payload_type = RTP_PT_MPEGAUDIO;
break;
case CODEC_ID_MJPEG:
payload_type = RTP_PT_JPEG;
break;
case CODEC_ID_MPEG1VIDEO:
payload_type = RTP_PT_MPEGVIDEO;
break;
case CODEC_ID_MPEG2TS:
payload_type = RTP_PT_MPEG2TS;
break;
default:
break;
}
return payload_type;
}
@ -216,7 +270,7 @@ static int rtcp_parse_packet(RTPDemuxContext *s, const unsigned char *buf, int l
* MPEG2TS streams to indicate that they should be demuxed inside the
* rtp demux (otherwise CODEC_ID_MPEG2TS packets are returned)
*/
RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_type)
RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_type, rtp_payload_data_t *rtp_payload_data)
{
RTPDemuxContext *s;
@ -228,7 +282,8 @@ RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_t
s->first_rtcp_ntp_time = AV_NOPTS_VALUE;
s->ic = s1;
s->st = st;
if (payload_type == RTP_PT_MPEG2TS) {
s->rtp_payload_data = rtp_payload_data;
if (!strcmp(AVRtpPayloadTypes[payload_type].enc_name, "MP2T")) {
s->ts = mpegts_parse_open(s->ic);
if (s->ts == NULL) {
av_free(s);
@ -250,6 +305,57 @@ RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_t
return s;
}
static int rtp_parse_mp4_au(RTPDemuxContext *s, const uint8_t *buf)
{
AVCodecContext codec;
int au_headers_length, au_header_size, i;
GetBitContext getbitcontext;
rtp_payload_data_t *infos;
infos = s->rtp_payload_data;
if (infos == NULL)
return -1;
codec = s->st->codec;
/* decode the first 2 bytes where are stored the AUHeader sections
length in bits */
au_headers_length = BE_16(buf);
if (au_headers_length > RTP_MAX_PACKET_LENGTH)
return -1;
infos->au_headers_length_bytes = (au_headers_length + 7) / 8;
/* skip AU headers length section (2 bytes) */
buf += 2;
init_get_bits(&getbitcontext, buf, infos->au_headers_length_bytes * 8);
/* XXX: Wrong if optionnal additional sections are present (cts, dts etc...) */
au_header_size = infos->sizelength + infos->indexlength;
if (au_header_size <= 0 || (au_headers_length % au_header_size != 0))
return -1;
infos->nb_au_headers = au_headers_length / au_header_size;
infos->au_headers = av_malloc(sizeof(struct AUHeaders) * infos->nb_au_headers);
/* XXX: We handle multiple AU Section as only one (need to fix this for interleaving)
In my test, the faad decoder doesnt behave correctly when sending each AU one by one
but does when sending the whole as one big packet... */
infos->au_headers[0].size = 0;
infos->au_headers[0].index = 0;
for (i = 0; i < infos->nb_au_headers; ++i) {
infos->au_headers[0].size += get_bits_long(&getbitcontext, infos->sizelength);
infos->au_headers[0].index = get_bits_long(&getbitcontext, infos->indexlength);
}
infos->nb_au_headers = 1;
return 0;
}
/**
* Parse an RTP or RTCP packet directly sent as a buffer.
* @param s RTP parse context.
@ -304,8 +410,8 @@ int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt,
av_log(&s->st->codec, AV_LOG_ERROR, "RTP: PT=%02x: bad cseq %04x expected=%04x\n",
payload_type, seq, ((s->seq + 1) & 0xffff));
}
s->seq = seq;
#endif
s->seq = seq;
len -= 12;
buf += 12;
@ -370,6 +476,28 @@ int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt,
pkt->pts = addend + delta_timestamp;
}
break;
case CODEC_ID_MPEG4:
pkt->pts = timestamp;
break;
case CODEC_ID_MPEG4AAC:
if (rtp_parse_mp4_au(s, buf))
return -1;
rtp_payload_data_t *infos = s->rtp_payload_data;
if (infos == NULL)
return -1;
buf += infos->au_headers_length_bytes + 2;
len -= infos->au_headers_length_bytes + 2;
/* XXX: Fixme we only handle the case where rtp_parse_mp4_au define
one au_header */
av_new_packet(pkt, infos->au_headers[0].size);
memcpy(pkt->data, buf, infos->au_headers[0].size);
buf += infos->au_headers[0].size;
len -= infos->au_headers[0].size;
s->read_buf_size = len;
s->buf_ptr = (char *)buf;
pkt->stream_index = s->st->index;
return 0;
default:
/* no timestamp info yet */
break;
@ -381,7 +509,7 @@ int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt,
void rtp_parse_close(RTPDemuxContext *s)
{
if (s->payload_type == RTP_PT_MPEG2TS) {
if (!strcmp(AVRtpPayloadTypes[s->payload_type].enc_name, "MP2T")) {
mpegts_parse_close(s->ts);
}
av_free(s);

View File

@ -19,22 +19,6 @@
#ifndef RTP_H
#define RTP_H
enum RTPPayloadType {
RTP_PT_ULAW = 0,
RTP_PT_GSM = 3,
RTP_PT_G723 = 4,
RTP_PT_ALAW = 8,
RTP_PT_S16BE_STEREO = 10,
RTP_PT_S16BE_MONO = 11,
RTP_PT_MPEGAUDIO = 14,
RTP_PT_JPEG = 26,
RTP_PT_H261 = 31,
RTP_PT_MPEGVIDEO = 32,
RTP_PT_MPEG2TS = 33,
RTP_PT_H263 = 34, /* old H263 encapsulation */
RTP_PT_PRIVATE = 96,
};
#define RTP_MIN_PACKET_LENGTH 12
#define RTP_MAX_PACKET_LENGTH 1500 /* XXX: suppress this define */
@ -43,8 +27,8 @@ int rtp_get_codec_info(AVCodecContext *codec, int payload_type);
int rtp_get_payload_type(AVCodecContext *codec);
typedef struct RTPDemuxContext RTPDemuxContext;
RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_type);
typedef struct rtp_payload_data_s rtp_payload_data_s;
RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_type, rtp_payload_data_s *rtp_payload_data);
int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt,
const uint8_t *buf, int len);
void rtp_parse_close(RTPDemuxContext *s);
@ -58,4 +42,82 @@ void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd);
extern URLProtocol rtp_protocol;
#define RTP_PT_PRIVATE 96
#define RTP_VERSION 2
#define RTP_MAX_SDES 256 /* maximum text length for SDES */
/* RTCP paquets use 0.5 % of the bandwidth */
#define RTCP_TX_RATIO_NUM 5
#define RTCP_TX_RATIO_DEN 1000
/* Structure listing usefull vars to parse RTP packet payload*/
typedef struct rtp_payload_data_s
{
int sizelength;
int indexlength;
int indexdeltalength;
int profile_level_id;
int streamtype;
int objecttype;
char *mode;
/* mpeg 4 AU headers */
struct AUHeaders {
int size;
int index;
int cts_flag;
int cts;
int dts_flag;
int dts;
int rap_flag;
int streamstate;
} *au_headers;
int nb_au_headers;
int au_headers_length_bytes;
int cur_au_index;
} rtp_payload_data_t;
typedef struct AVRtpPayloadType_s
{
int pt;
const char enc_name[50]; /* XXX: why 50 ? */
enum CodecType codec_type;
enum CodecID codec_id;
int clock_rate;
int audio_channels;
} AVRtpPayloadType_t;
typedef struct AVRtpDynamicPayloadType_s /* payload type >= 96 */
{
const char enc_name[50]; /* XXX: still why 50 ? ;-) */
enum CodecType codec_type;
enum CodecID codec_id;
} AVRtpDynamicPayloadType_t;
typedef enum {
RTCP_SR = 200,
RTCP_RR = 201,
RTCP_SDES = 202,
RTCP_BYE = 203,
RTCP_APP = 204
} rtcp_type_t;
typedef enum {
RTCP_SDES_END = 0,
RTCP_SDES_CNAME = 1,
RTCP_SDES_NAME = 2,
RTCP_SDES_EMAIL = 3,
RTCP_SDES_PHONE = 4,
RTCP_SDES_LOC = 5,
RTCP_SDES_TOOL = 6,
RTCP_SDES_NOTE = 7,
RTCP_SDES_PRIV = 8,
RTCP_SDES_IMG = 9,
RTCP_SDES_DOOR = 10,
RTCP_SDES_SOURCE = 11
} rtcp_sdes_type_t;
extern AVRtpPayloadType_t AVRtpPayloadTypes[];
extern AVRtpDynamicPayloadType_t AVRtpDynamicPayloadTypes[];
#endif /* RTP_H */

View File

@ -66,22 +66,15 @@ typedef struct RTSPStream {
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 */
rtp_payload_data_t rtp_payload_data; /* rtp payload parsing infos from SDP */
} RTSPStream;
static int rtsp_read_play(AVFormatContext *s);
/* XXX: currently, the only way to change the protocols consists in
changing this variable */
#if 0
int rtsp_default_protocols = (1 << RTSP_PROTOCOL_RTP_TCP) | (1 << RTSP_PROTOCOL_RTP_UDP) | (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST);
#else
/* try it if a proxy is used */
int rtsp_default_protocols = (1 << RTSP_PROTOCOL_RTP_TCP);
#endif
/* if non zero, then set a range for RTP ports */
int rtsp_rtp_port_min = 0;
int rtsp_rtp_port_max = 0;
int rtsp_default_protocols = (1 << RTSP_PROTOCOL_RTP_UDP);
FFRTSPCallback *ff_rtsp_callback = NULL;
@ -113,6 +106,8 @@ static void get_word_sep(char *buf, int buf_size, const char *sep,
char *q;
p = *pp;
if (*p == '/')
p++;
skip_spaces(&p);
q = buf;
while (!strchr(sep, *p) && *p != '\0') {
@ -145,18 +140,67 @@ static void get_word(char *buf, int buf_size, const char **pp)
/* parse the rtpmap description: <codec_name>/<clock_rate>[/<other
params>] */
static int sdp_parse_rtpmap(AVCodecContext *codec, const char *p)
static int sdp_parse_rtpmap(AVCodecContext *codec, int payload_type, const char *p)
{
char buf[256];
int i;
AVCodec *c;
char *c_name;
/* codec name */
/* Loop into AVRtpDynamicPayloadTypes[] and AVRtpPayloadTypes[] and
see if we can handle this kind of payload */
get_word_sep(buf, sizeof(buf), "/", &p);
if (!strcmp(buf, "MP4V-ES")) {
codec->codec_id = CODEC_ID_MPEG4;
return 0;
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;
break;
}
} else {
return -1;
/* We are in a standard case ( from http://www.iana.org/assignments/rtp-parameters) */
/* search into AVRtpPayloadTypes[] */
for (i = 0; AVRtpPayloadTypes[i].pt >= 0; ++i)
if (!strcmp(buf, AVRtpPayloadTypes[i].enc_name) && (codec->codec_type == AVRtpPayloadTypes[i].codec_type)){
codec->codec_id = AVRtpPayloadTypes[i].codec_id;
break;
}
}
c = avcodec_find_decoder(codec->codec_id);
if (c && c->name)
c_name = (char *)c->name;
else
c_name = (char *)NULL;
if (c_name) {
get_word_sep(buf, sizeof(buf), "/", &p);
i = atoi(buf);
switch (codec->codec_type) {
case CODEC_TYPE_AUDIO:
av_log(codec, AV_LOG_DEBUG, " audio codec set to : %s\n", c_name);
codec->sample_rate = RTSP_DEFAULT_AUDIO_SAMPLERATE;
codec->channels = RTSP_DEFAULT_NB_AUDIO_CHANNELS;
if (i > 0) {
codec->sample_rate = i;
get_word_sep(buf, sizeof(buf), "/", &p);
i = atoi(buf);
if (i > 0)
codec->channels = i;
}
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);
break;
case CODEC_TYPE_VIDEO:
av_log(codec, AV_LOG_DEBUG, " video codec set to : %s\n", c_name);
break;
default:
break;
}
return 0;
}
return -1;
}
/* return the length and optionnaly the data */
@ -188,11 +232,58 @@ static int hex_to_data(uint8_t *data, const char *p)
return len;
}
static void sdp_parse_fmtp(AVCodecContext *codec, const char *p)
static void sdp_parse_fmtp_config(AVCodecContext *codec, char *attr, char *value)
{
switch (codec->codec_id) {
case CODEC_ID_MPEG4:
case CODEC_ID_MPEG4AAC:
if (!strcmp(attr, "config")) {
/* decode the hexa encoded parameter */
int len = hex_to_data(NULL, value);
codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE);
if (!codec->extradata)
return;
codec->extradata_size = len;
hex_to_data(codec->extradata, value);
}
break;
default:
break;
}
return;
}
typedef struct attrname_map
{
char *str;
uint16_t type;
uint32_t offset;
} attrname_map_t;
/* All known fmtp parmeters and the corresping RTPAttrTypeEnum */
#define ATTR_NAME_TYPE_INT 0
#define ATTR_NAME_TYPE_STR 1
static attrname_map_t attr_names[]=
{
{"SizeLength", ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, sizelength)},
{"IndexLength", ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, indexlength)},
{"IndexDeltaLength", ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, indexdeltalength)},
{"profile-level-id", ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, profile_level_id)},
{"StreamType", ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, streamtype)},
{"mode", ATTR_NAME_TYPE_STR, offsetof(rtp_payload_data_t, mode)},
{NULL, -1, -1},
};
/* parse a SDP line and save stream attributes */
static void sdp_parse_fmtp(AVStream *st, const char *p)
{
char attr[256];
char value[4096];
int len;
int i;
RTSPStream *rtsp_st = st->priv_data;
AVCodecContext *codec = &st->codec;
rtp_payload_data_t *rtp_payload_data = &rtsp_st->rtp_payload_data;
/* loop on each attribute */
for(;;) {
@ -205,25 +296,17 @@ static void sdp_parse_fmtp(AVCodecContext *codec, const char *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 + FF_INPUT_BUFFER_PADDING_SIZE);
if (!codec->extradata)
goto fail;
codec->extradata_size = len;
hex_to_data(codec->extradata, value);
}
break;
default:
/* ignore data for other codecs */
break;
/* 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 */
for (i = 0; attr_names[i].str; ++i) {
if (!strcasecmp(attr, attr_names[i].str)) {
if (attr_names[i].type == ATTR_NAME_TYPE_INT)
*(int *)((char *)rtp_payload_data + attr_names[i].offset) = atoi(value);
else if (attr_names[i].type == ATTR_NAME_TYPE_STR)
*(char **)((char *)rtp_payload_data + attr_names[i].offset) = av_strdup(value);
}
}
fail: ;
// printf("'%s' = '%s'\n", attr, value);
}
}
@ -314,7 +397,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
get_word(buf1, sizeof(buf1), &p); /* format list */
rtsp_st->sdp_payload_type = atoi(buf1);
if (rtsp_st->sdp_payload_type == RTP_PT_MPEG2TS) {
if (!strcmp(AVRtpPayloadTypes[rtsp_st->sdp_payload_type].enc_name, "MP2T")) {
/* no corresponding stream */
} else {
st = av_new_stream(s, 0);
@ -323,7 +406,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
st->priv_data = rtsp_st;
rtsp_st->stream_index = st->index;
st->codec.codec_type = codec_type;
if (rtsp_st->sdp_payload_type < 96) {
if (rtsp_st->sdp_payload_type < RTP_PT_PRIVATE) {
/* if standard payload type, we can find the codec right now */
rtp_get_codec_info(&st->codec, rtsp_st->sdp_payload_type);
}
@ -355,7 +438,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, p);
sdp_parse_rtpmap(&st->codec, payload_type, p);
}
}
} else if (strstart(p, "fmtp:", &p)) {
@ -366,7 +449,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_fmtp(&st->codec, p);
sdp_parse_fmtp(st, p);
}
}
}
@ -715,7 +798,7 @@ static int rtsp_read_header(AVFormatContext *s,
RTSPState *rt = s->priv_data;
char host[1024], path[1024], tcpname[1024], cmd[2048];
URLContext *rtsp_hd;
int port, i, ret, err;
int port, i, j, ret, err;
RTSPHeader reply1, *reply = &reply1;
unsigned char *content = NULL;
RTSPStream *rtsp_st;
@ -763,7 +846,8 @@ static int rtsp_read_header(AVFormatContext *s,
/* 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;i<rt->nb_rtsp_streams;i++) {
for(j = RTSP_RTP_PORT_MIN, i = 0; i < rt->nb_rtsp_streams; ++i) {
char transport[2048];
rtsp_st = rt->rtsp_streams[i];
@ -774,22 +858,24 @@ static int rtsp_read_header(AVFormatContext *s,
/* RTP/UDP */
if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP)) {
char buf[256];
int j;
/* first try in specified port range */
if (rtsp_rtp_port_min != 0) {
for(j=rtsp_rtp_port_min;j<=rtsp_rtp_port_max;j++) {
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_RDONLY) == 0) {
j += 2; /* we will use two port by rtp stream (rtp and rtcp) */
goto rtp_opened;
}
}
}
/* then try on any port */
if (url_open(&rtsp_st->rtp_handle, "rtp://", URL_RDONLY) < 0) {
err = AVERROR_INVALIDDATA;
goto fail;
}
/* then try on any port
** if (url_open(&rtsp_st->rtp_handle, "rtp://", URL_RDONLY) < 0) {
** err = AVERROR_INVALIDDATA;
** goto fail;
** }
*/
rtp_opened:
port = rtp_get_local_port(rtsp_st->rtp_handle);
@ -801,14 +887,14 @@ static int rtsp_read_header(AVFormatContext *s,
}
/* RTP/TCP */
if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_TCP)) {
else 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)) {
else if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST)) {
if (transport[0] != '\0')
pstrcat(transport, sizeof(transport), ",");
snprintf(transport + strlen(transport),
@ -887,7 +973,8 @@ 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_ctx = rtp_parse_open(s, st, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data);
if (!rtsp_st->rtp_ctx) {
err = AVERROR_NOMEM;
goto fail;
@ -1233,7 +1320,7 @@ 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_ctx = rtp_parse_open(s, st, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data);
if (!rtsp_st->rtp_ctx) {
err = AVERROR_NOMEM;
goto fail;

View File

@ -35,6 +35,10 @@ enum RTSPProtocol {
#define RTSP_DEFAULT_PORT 554
#define RTSP_MAX_TRANSPORTS 8
#define RTSP_TCP_MAX_PACKET_SIZE 1472
#define RTSP_DEFAULT_NB_AUDIO_CHANNELS 2
#define RTSP_DEFAULT_AUDIO_SAMPLERATE 44100
#define RTSP_RTP_PORT_MIN 5000
#define RTSP_RTP_PORT_MAX 10000
typedef struct RTSPTransportField {
int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */