#include "rtpdec_formats.h"
#include "internal.h"
+#include "libavutil/attributes.h"
#include "libavutil/avstring.h"
#include "libavcodec/get_bits.h"
+#define MAX_AAC_HBR_FRAME_SIZE 8191
+
/** Structure listing useful vars to parse RTP packet payload */
struct PayloadContext {
int sizelength;
int nb_au_headers;
int au_headers_length_bytes;
int cur_au_index;
+
+ uint8_t buf[FFMAX(RTP_MAX_PACKET_LENGTH, MAX_AAC_HBR_FRAME_SIZE)];
+ int buf_pos, buf_size;
+ uint32_t timestamp;
};
-typedef struct {
+typedef struct AttrNameMap {
const char *str;
uint16_t type;
uint32_t offset;
{ NULL, -1, -1 },
};
-static PayloadContext *new_context(void)
-{
- return av_mallocz(sizeof(PayloadContext));
-}
-
-static void free_context(PayloadContext *data)
+static void close_context(PayloadContext *data)
{
av_free(data->au_headers);
av_free(data->mode);
- av_free(data);
}
-static int parse_fmtp_config(AVCodecContext *codec, char *value)
+static int parse_fmtp_config(AVCodecContext *codec, const char *value)
{
/* decode the hexa encoded parameter */
int len = ff_hex_to_data(NULL, value);
return 0;
}
-static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf)
+static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf, int len)
{
int au_headers_length, au_header_size, i;
GetBitContext getbitcontext;
+ if (len < 2)
+ return AVERROR_INVALIDDATA;
+
/* decode the first 2 bytes where the AUHeader sections are stored
length in bits */
au_headers_length = AV_RB16(buf);
/* skip AU headers length section (2 bytes) */
buf += 2;
+ len -= 2;
+
+ if (len < data->au_headers_length_bytes)
+ return AVERROR_INVALIDDATA;
init_get_bits(&getbitcontext, buf, data->au_headers_length_bytes * 8);
data->au_headers_allocated = data->nb_au_headers;
}
- /* XXX: We handle multiple AU Section as only one (need to fix this for interleaving)
- In my test, the FAAD decoder does not behave correctly when sending each AU one by one
- but does when sending the whole as one big packet... */
- data->au_headers[0].size = 0;
- data->au_headers[0].index = 0;
for (i = 0; i < data->nb_au_headers; ++i) {
- data->au_headers[0].size += get_bits_long(&getbitcontext, data->sizelength);
- data->au_headers[0].index = get_bits_long(&getbitcontext, data->indexlength);
+ data->au_headers[i].size = get_bits_long(&getbitcontext, data->sizelength);
+ data->au_headers[i].index = get_bits_long(&getbitcontext, data->indexlength);
}
- data->nb_au_headers = 1;
-
return 0;
}
int flags)
{
int ret;
- if (rtp_parse_mp4_au(data, buf))
+
+ if (!buf) {
+ if (data->cur_au_index > data->nb_au_headers) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid parser state\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (data->buf_size - data->buf_pos < data->au_headers[data->cur_au_index].size) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid AU size\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if ((ret = av_new_packet(pkt, data->au_headers[data->cur_au_index].size)) < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Out of memory\n");
+ return ret;
+ }
+ memcpy(pkt->data, &data->buf[data->buf_pos], data->au_headers[data->cur_au_index].size);
+ data->buf_pos += data->au_headers[data->cur_au_index].size;
+ pkt->stream_index = st->index;
+ data->cur_au_index++;
+
+ if (data->cur_au_index == data->nb_au_headers) {
+ data->buf_pos = 0;
+ return 0;
+ }
+
+ return 1;
+ }
+
+ if (rtp_parse_mp4_au(data, buf, len)) {
+ av_log(ctx, AV_LOG_ERROR, "Error parsing AU headers\n");
return -1;
+ }
buf += data->au_headers_length_bytes + 2;
len -= data->au_headers_length_bytes + 2;
+ if (data->nb_au_headers == 1 && len < data->au_headers[0].size) {
+ /* Packet is fragmented */
- /* XXX: Fixme we only handle the case where rtp_parse_mp4_au define
- one au_header */
- if ((ret = av_new_packet(pkt, data->au_headers[0].size)) < 0)
+ if (!data->buf_pos) {
+ if (data->au_headers[0].size > MAX_AAC_HBR_FRAME_SIZE) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid AU size\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ data->buf_size = data->au_headers[0].size;
+ data->timestamp = *timestamp;
+ }
+
+ if (data->timestamp != *timestamp ||
+ data->au_headers[0].size != data->buf_size ||
+ data->buf_pos + len > MAX_AAC_HBR_FRAME_SIZE) {
+ data->buf_pos = 0;
+ data->buf_size = 0;
+ av_log(ctx, AV_LOG_ERROR, "Invalid packet received\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ memcpy(&data->buf[data->buf_pos], buf, len);
+ data->buf_pos += len;
+
+ if (!(flags & RTP_FLAG_MARKER))
+ return AVERROR(EAGAIN);
+
+ if (data->buf_pos != data->buf_size) {
+ data->buf_pos = 0;
+ av_log(ctx, AV_LOG_ERROR, "Missed some packets, discarding frame\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ data->buf_pos = 0;
+ ret = av_new_packet(pkt, data->buf_size);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Out of memory\n");
+ return ret;
+ }
+ pkt->stream_index = st->index;
+
+ memcpy(pkt->data, data->buf, data->buf_size);
+
+ return 0;
+ }
+
+ if (len < data->au_headers[0].size) {
+ av_log(ctx, AV_LOG_ERROR, "First AU larger than packet size\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if ((ret = av_new_packet(pkt, data->au_headers[0].size)) < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Out of memory\n");
return ret;
+ }
memcpy(pkt->data, buf, data->au_headers[0].size);
-
+ len -= data->au_headers[0].size;
+ buf += data->au_headers[0].size;
pkt->stream_index = st->index;
+
+ if (len > 0 && data->nb_au_headers > 1) {
+ data->buf_size = FFMIN(len, sizeof(data->buf));
+ memcpy(data->buf, buf, data->buf_size);
+ data->cur_au_index = 1;
+ data->buf_pos = 0;
+ return 1;
+ }
+
return 0;
}
-static int parse_fmtp(AVStream *stream, PayloadContext *data,
- char *attr, char *value)
+static int parse_fmtp(AVFormatContext *s,
+ AVStream *stream, PayloadContext *data,
+ const char *attr, const char *value)
{
AVCodecContext *codec = stream->codec;
int res, i;
return 0;
if (av_strstart(line, "fmtp:", &p))
- return ff_parse_fmtp(s->streams[st_index], data, p, parse_fmtp);
+ return ff_parse_fmtp(s, s->streams[st_index], data, p, parse_fmtp);
return 0;
}
.enc_name = "MP4V-ES",
.codec_type = AVMEDIA_TYPE_VIDEO,
.codec_id = AV_CODEC_ID_MPEG4,
+ .need_parsing = AVSTREAM_PARSE_FULL,
+ .priv_data_size = sizeof(PayloadContext),
.parse_sdp_a_line = parse_sdp_line,
};
.enc_name = "mpeg4-generic",
.codec_type = AVMEDIA_TYPE_AUDIO,
.codec_id = AV_CODEC_ID_AAC,
+ .priv_data_size = sizeof(PayloadContext),
.parse_sdp_a_line = parse_sdp_line,
- .alloc = new_context,
- .free = free_context,
- .parse_packet = aac_parse_packet
+ .close = close_context,
+ .parse_packet = aac_parse_packet,
};