diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 16170d4..5b3fa29 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -566,6 +566,7 @@ typedef struct AVFormatContext { #define AVFMT_FLAG_GENPTS 0x0001 ///< Generate missing pts even if it requires parsing future frames. #define AVFMT_FLAG_IGNIDX 0x0002 ///< Ignore index. #define AVFMT_FLAG_NONBLOCK 0x0004 ///< Do not block when reading packets from input. +#define AVFMT_FLAG_LATM 0x0008 ///< Generate LATM rtp stream for mpeg4audio codec. int loop_input; /** decoding: size of data to probe; encoding: unused. */ diff --git a/libavformat/avio.c b/libavformat/avio.c index b6329a9..0e1c847 100644 --- a/libavformat/avio.c +++ b/libavformat/avio.c @@ -87,6 +87,7 @@ int url_open_protocol (URLContext **puc, struct URLProtocol *up, uc->flags = flags; uc->is_streamed = 0; /* default = not streamed */ uc->max_packet_size = 0; /* default: stream file */ + uc->rtp_payload_type = -1; /* default: depends on codec */ err = up->url_open(uc, filename, flags); if (err < 0) { av_free(uc); @@ -232,6 +233,11 @@ int url_get_max_packet_size(URLContext *h) return h->max_packet_size; } +int url_get_rtp_payload_type(URLContext *h) +{ + return h->rtp_payload_type; +} + void url_get_filename(URLContext *h, char *buf, int buf_size) { av_strlcpy(buf, h->filename, buf_size); diff --git a/libavformat/avio.h b/libavformat/avio.h index d44e300..f8078e5 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -49,6 +49,7 @@ typedef struct URLContext { int flags; int is_streamed; /**< true if streamed (no seek possible), default = false */ int max_packet_size; /**< if non zero, the stream is packetized with this max packet size */ + int rtp_payload_type; /**< if non zero, the rtp stream is packetized with this payload type */ void *priv_data; char *filename; /**< specified filename */ } URLContext; @@ -94,6 +95,17 @@ int url_get_file_handle(URLContext *h); * @return maximum packet size in bytes */ int url_get_max_packet_size(URLContext *h); + +/** + * Return the rtp payload type associated to packetized file + * handle. If the file is not packetized (stream like HTTP or file on + * disk), then -1 is returned. + * + * @param h file handle + * @return rtp payload type + */ +int url_get_rtp_payload_type(URLContext *h); + void url_get_filename(URLContext *h, char *buf, int buf_size); /** @@ -198,6 +210,7 @@ typedef struct { int write_flag; /**< true if open for writing */ int is_streamed; int max_packet_size; + int rtp_payload_type; unsigned long checksum; unsigned char *checksum_ptr; unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size); @@ -355,6 +368,16 @@ URLContext *url_fileno(ByteIOContext *s); */ int url_fget_max_packet_size(ByteIOContext *s); +/** + * Return the payload type, associated to rtp packetized buffered file + * handle. If the file is not packetized (stream like http or file on + * disk), then -1 is returned. + * + * @param s buffered file handle + * @return rtp payload type + */ +int url_fget_rtp_payload_type(ByteIOContext *s); + int url_open_buf(ByteIOContext **s, uint8_t *buf, int buf_size, int flags); /** return the written or read size */ diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index f270139..d734efc 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -52,6 +52,7 @@ int init_put_byte(ByteIOContext *s, s->error = 0; s->is_streamed = 0; s->max_packet_size = 0; + s->rtp_payload_type = -1; s->update_checksum= NULL; if(!read_packet && !write_flag){ s->pos = buffer_size; @@ -529,10 +530,11 @@ uint64_t ff_get_v(ByteIOContext *bc){ int url_fdopen(ByteIOContext **s, URLContext *h) { uint8_t *buffer; - int buffer_size, max_packet_size; + int buffer_size, max_packet_size, rtp_payload_type; max_packet_size = url_get_max_packet_size(h); + rtp_payload_type = url_get_rtp_payload_type(h); if (max_packet_size) { buffer_size = max_packet_size; /* no need to bufferize more than one packet */ } else { @@ -557,6 +559,7 @@ int url_fdopen(ByteIOContext **s, URLContext *h) } (*s)->is_streamed = h->is_streamed; (*s)->max_packet_size = max_packet_size; + (*s)->rtp_payload_type = rtp_payload_type; if(h->prot) { (*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause; (*s)->read_seek = (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek; @@ -666,6 +669,11 @@ int url_fget_max_packet_size(ByteIOContext *s) return s->max_packet_size; } +int url_fget_rtp_payload_type(ByteIOContext *s) +{ + return s->rtp_payload_type; +} + int av_url_read_fpause(ByteIOContext *s, int pause) { if (!s->read_pause) diff --git a/libavformat/options.c b/libavformat/options.c index 795d02b..b96d1dd 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -46,6 +46,7 @@ static const AVOption options[]={ {"fflags", NULL, OFFSET(flags), FF_OPT_TYPE_FLAGS, DEFAULT, INT_MIN, INT_MAX, D|E, "fflags"}, {"ignidx", "ignore index", 0, FF_OPT_TYPE_CONST, AVFMT_FLAG_IGNIDX, INT_MIN, INT_MAX, D, "fflags"}, {"genpts", "generate pts", 0, FF_OPT_TYPE_CONST, AVFMT_FLAG_GENPTS, INT_MIN, INT_MAX, D, "fflags"}, +{"latm", "packetize mpeg4audio to LATM (RFC 3016)", 0, FF_OPT_TYPE_CONST, AVFMT_FLAG_LATM, INT_MIN, INT_MAX, D, "fflags"}, #if LIBAVFORMAT_VERSION_INT < (53<<16) {"track", " set the track number", OFFSET(track), FF_OPT_TYPE_INT, DEFAULT, 0, INT_MAX, E}, {"year", "set the year", OFFSET(year), FF_OPT_TYPE_INT, DEFAULT, INT_MIN, INT_MAX, E}, diff --git a/libavformat/rtp_aac.c b/libavformat/rtp_aac.c index e19b286..8852857 100644 --- a/libavformat/rtp_aac.c +++ b/libavformat/rtp_aac.c @@ -83,3 +83,45 @@ void ff_rtp_send_aac(AVFormatContext *s1, const uint8_t *buff, int size) } } } + + + + +/* latm muxer */ +void ff_rtp_send_aac_latm(AVFormatContext *s1, const uint8_t *buff, int size) +{ + RTPMuxContext *s = s1->priv_data; + int len, max_packet_size; + uint8_t *p = s->buf; + int latmhdrsize; + + /* skip ADTS header, if present */ + if ((s1->streams[0]->codec->extradata_size) == 0) { + size -= 7; + buff += 7; + } + + latmhdrsize = size/0xff + 1; + max_packet_size = s->max_payload_size - latmhdrsize; + s->timestamp = s->cur_timestamp; + + while (size > 0) { + len = FFMIN(size, max_packet_size); + memcpy(p + latmhdrsize, buff, len); + if(latmhdrsize) { + /* write latm header in 1st packet */ + int tmp = size; + while (tmp > 0xfe){ + *p = 0xff; + p++; + tmp -= 0xff; + } + *p = tmp; + } + + ff_rtp_send_data(s1, s->buf, len + latmhdrsize, (len == size)); + size -= len; + buff += len; + latmhdrsize = 0; + } +} diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index 2a0770e..d9788c4 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -83,9 +83,12 @@ static int rtp_write_header(AVFormatContext *s1) return -1; } - payload_type = ff_rtp_get_payload_type(st->codec); - if (payload_type < 0) - payload_type = RTP_PT_PRIVATE; /* private payload type */ + payload_type = url_fget_rtp_payload_type(s1->pb); + if (payload_type < 0) { + payload_type = ff_rtp_get_payload_type(st->codec); + if (payload_type < 0) + payload_type = RTP_PT_PRIVATE; /* private payload type */ + } s->payload_type = payload_type; // following 2 FIXMEs could be set based on the current time, there is normally no info leak, as RTP will likely be transmitted immediately @@ -383,7 +386,10 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) ff_rtp_send_mpegvideo(s1, buf1, size); break; case CODEC_ID_AAC: - ff_rtp_send_aac(s1, buf1, size); + if(s1->flags & AVFMT_FLAG_LATM) + ff_rtp_send_aac_latm(s1, buf1, size); + else + ff_rtp_send_aac(s1, buf1, size); break; case CODEC_ID_AMR_NB: case CODEC_ID_AMR_WB: diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h index 5710160..905667f 100644 --- a/libavformat/rtpenc.h +++ b/libavformat/rtpenc.h @@ -59,6 +59,7 @@ void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m); void ff_rtp_send_h264(AVFormatContext *s1, const uint8_t *buf1, int size); void ff_rtp_send_h263(AVFormatContext *s1, const uint8_t *buf1, int size); void ff_rtp_send_aac(AVFormatContext *s1, const uint8_t *buff, int size); +void ff_rtp_send_aac_latm(AVFormatContext *s1, const uint8_t *buff, int size); void ff_rtp_send_amr(AVFormatContext *s1, const uint8_t *buff, int size); void ff_rtp_send_mpegvideo(AVFormatContext *s1, const uint8_t *buf1, int size); diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index 9d80ddf..ac0b3bd 100644 --- a/libavformat/rtpproto.c +++ b/libavformat/rtpproto.c @@ -98,7 +98,7 @@ static void url_add_option(char *buf, int buf_size, const char *fmt, ...) static void build_udp_url(char *buf, int buf_size, const char *hostname, int port, int local_port, int ttl, - int max_packet_size) + int max_packet_size, int rtp_payload_type) { snprintf(buf, buf_size, "udp://%s:%d", hostname, port); if (local_port >= 0) @@ -107,6 +107,8 @@ static void build_udp_url(char *buf, int buf_size, url_add_option(buf, buf_size, "ttl=%d", ttl); if (max_packet_size >=0) url_add_option(buf, buf_size, "pkt_size=%d", max_packet_size); + if (rtp_payload_type >=0) + url_add_option(buf, buf_size, "payload_type=%d", rtp_payload_type); } /** @@ -120,7 +122,7 @@ static void build_udp_url(char *buf, int buf_size, static int rtp_open(URLContext *h, const char *uri, int flags) { RTPContext *s; - int port, is_output, ttl, local_port, max_packet_size; + int port, is_output, ttl, local_port, max_packet_size, rtp_payload_type; char hostname[256]; char buf[1024]; char path[1024]; @@ -139,6 +141,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags) ttl = -1; local_port = -1; max_packet_size = -1; + rtp_payload_type = -1; p = strchr(uri, '?'); if (p) { @@ -151,10 +154,13 @@ static int rtp_open(URLContext *h, const char *uri, int flags) if (find_info_tag(buf, sizeof(buf), "pkt_size", p)) { max_packet_size = strtol(buf, NULL, 10); } + if (find_info_tag(buf, sizeof(buf), "payload_type", p)) { + rtp_payload_type = strtol(buf, NULL, 10); + } } build_udp_url(buf, sizeof(buf), - hostname, port, local_port, ttl, max_packet_size); + hostname, port, local_port, ttl, max_packet_size, rtp_payload_type); if (url_open(&s->rtp_hd, buf, flags) < 0) goto fail; local_port = udp_get_local_port(s->rtp_hd); @@ -163,7 +169,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags) /* well, should suppress localport in path */ build_udp_url(buf, sizeof(buf), - hostname, port + 1, local_port + 1, ttl, max_packet_size); + hostname, port + 1, local_port + 1, ttl, max_packet_size, rtp_payload_type); if (url_open(&s->rtcp_hd, buf, flags) < 0) goto fail; @@ -172,6 +178,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags) s->rtp_fd = url_get_file_handle(s->rtp_hd); s->rtcp_fd = url_get_file_handle(s->rtcp_hd); + h->rtp_payload_type = url_get_rtp_payload_type(s->rtp_hd); h->max_packet_size = url_get_max_packet_size(s->rtp_hd); h->is_streamed = 1; return 0; diff --git a/libavformat/sdp.c b/libavformat/sdp.c index 67b10a2..cd0d443 100644 --- a/libavformat/sdp.c +++ b/libavformat/sdp.c @@ -20,6 +20,8 @@ #include "libavutil/avstring.h" #include "libavutil/base64.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/mpeg4audio.h" #include "avformat.h" #include "internal.h" #include "avc.h" @@ -163,10 +165,46 @@ static char *extradata2config(AVCodecContext *c) return config; } -static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, int payload_type) +static char *extradata2latmConfig(AVCodecContext *c) { - char *config = NULL; + char *config; + unsigned char binConfig[6]; + int sample_rate_index, channels_index, objecttype; + GetBitContext gb; + + /* this for aac+, we have to look at extradata to detect AAC (not he-aac) samplerate and channels */ + init_get_bits(&gb, c->extradata, c->extradata_size * 8); + objecttype = get_bits(&gb, 5) - 1; + sample_rate_index = get_bits(&gb, 4); + channels_index = get_bits(&gb, 4); + //*channels = ff_mpeg4audio_channels[channels_index]; + //*sample_rate = ff_mpeg4audio_sample_rates[sample_rate_index]; + + + binConfig[0]=0x40; + binConfig[1]=0; + binConfig[2]=0x20|sample_rate_index; + binConfig[3]=channels_index<<4; + binConfig[4]=0x3f; + binConfig[5]=0xc0; + + config = av_malloc(10 + 12); + if (config == NULL) { + av_log(c, AV_LOG_ERROR, "Cannot allocate memory for the config info.\n"); + return NULL; + } + + memcpy(config, "; config=", 9); + ff_data_to_hex(config + 9, binConfig, 6); + config[9 + 12] = 0; + return config; +} + +static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, int payload_type, int latm) +{ + char *config = NULL; + switch (c->codec_id) { case CODEC_ID_H264: if (c->extradata_size) { @@ -192,7 +230,11 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, break; case CODEC_ID_AAC: if (c->extradata_size) { - config = extradata2config(c); + if(latm) { + config = extradata2latmConfig(c/*, &sample_rate, &channels*/); + } else { + config = extradata2config(c); + } } else { /* FIXME: maybe we can forge config information based on the * codec parameters... @@ -203,12 +245,20 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, if (config == NULL) { return NULL; } - av_strlcatf(buff, size, "a=rtpmap:%d MPEG4-GENERIC/%d/%d\r\n" - "a=fmtp:%d profile-level-id=1;" - "mode=AAC-hbr;sizelength=13;indexlength=3;" - "indexdeltalength=3%s\r\n", - payload_type, c->sample_rate, c->channels, - payload_type, config); + if(latm) { + av_strlcatf(buff, size, "a=rtpmap:%d MP4A-LATM/%d/%d\r\n" + "a=fmtp:%d profile-level-id=15;" + "object=2; cpresent=0%s\r\n", + payload_type, /*sample_rate, channels*/ c->sample_rate, c->channels, + payload_type, config); + } else { + av_strlcatf(buff, size, "a=rtpmap:%d MPEG4-GENERIC/%d/%d\r\n" + "a=fmtp:%d profile-level-id=1;" + "mode=AAC-hbr;sizelength=13;indexlength=3;" + "indexdeltalength=3%s\r\n", + payload_type, c->sample_rate, c->channels, + payload_type, config); + } break; case CODEC_ID_PCM_S16BE: if (payload_type >= 96) @@ -250,15 +300,9 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, return buff; } -static void sdp_write_media(char *buff, int size, AVCodecContext *c, const char *dest_addr, int port, int ttl) +static void sdp_write_media(char *buff, int size, AVCodecContext *c, const char *dest_addr, int port, int ttl, int payload_type, int latm) { const char *type; - int payload_type; - - payload_type = ff_rtp_get_payload_type(c); - if (payload_type < 0) { - payload_type = 96; /* FIXME: how to assign a private pt? rtp.c is broken too */ - } switch (c->codec_type) { case CODEC_TYPE_VIDEO : type = "video" ; break; @@ -273,14 +317,14 @@ static void sdp_write_media(char *buff, int size, AVCodecContext *c, const char av_strlcatf(buff, size, "b=AS:%d\r\n", c->bit_rate / 1000); } - sdp_write_media_attributes(buff, size, c, payload_type); + sdp_write_media_attributes(buff, size, c, payload_type, latm); } int avf_sdp_create(AVFormatContext *ac[], int n_files, char *buff, int size) { AVMetadataTag *title = av_metadata_get(ac[0]->metadata, "title", NULL, 0); struct sdp_session_level s; - int i, j, port, ttl; + int i, j, port, ttl, payload_type; char dst[32]; memset(buff, 0, size); @@ -306,9 +350,17 @@ int avf_sdp_create(AVFormatContext *ac[], int n_files, char *buff, int size) port = sdp_get_address(dst, sizeof(dst), &ttl, ac[i]->filename); } for (j = 0; j < ac[i]->nb_streams; j++) { + payload_type = url_fget_rtp_payload_type(ac[i]->pb); + if (payload_type < 0) { + payload_type = ff_rtp_get_payload_type(ac[i]->streams[j]->codec); + if (payload_type < 0) + payload_type = RTP_PT_PRIVATE; /* private payload type */ + } + sdp_write_media(buff, size, ac[i]->streams[j]->codec, dst[0] ? dst : NULL, - (port > 0) ? port + j * 2 : 0, ttl); + (port > 0) ? port + j * 2 : 0, ttl, + payload_type, ac[i]->flags & AVFMT_FLAG_LATM ? 1 : 0); if (port <= 0) { av_strlcatf(buff, size, "a=control:streamid=%d\r\n", i + j); diff --git a/libavformat/udp.c b/libavformat/udp.c index a89de00..89a8c4e 100644 --- a/libavformat/udp.c +++ b/libavformat/udp.c @@ -383,6 +383,9 @@ static int udp_open(URLContext *h, const char *uri, int flags) if (find_info_tag(buf, sizeof(buf), "buffer_size", p)) { s->buffer_size = strtol(buf, NULL, 10); } + if (find_info_tag(buf, sizeof(buf), "payload_type", p)) { + h->rtp_payload_type = strtol(buf, NULL, 10); + } } /* fill the dest addr */