* @author Romain Degez
*/
-#include "rtpdec_mpeg4.h"
+#include "rtpdec_formats.h"
#include "internal.h"
#include "libavutil/avstring.h"
+#include "libavcodec/get_bits.h"
+#include <strings.h>
-/* return the length and optionally the data */
-static int hex_to_data(uint8_t *data, const char *p)
+/** Structure listing useful vars to parse RTP packet payload*/
+struct PayloadContext
{
- int c, len, v;
-
- len = 0;
- v = 1;
- for (;;) {
- p += strspn(p, SPACE_CHARS);
- 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;
- }
+ 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 au_headers_allocated;
+ int nb_au_headers;
+ int au_headers_length_bytes;
+ int cur_au_index;
+};
+
+typedef struct {
+ const char *str;
+ uint16_t type;
+ uint32_t offset;
+} AttrNameMap;
+
+/* All known fmtp parameters and the corresponding RTPAttrTypeEnum */
+#define ATTR_NAME_TYPE_INT 0
+#define ATTR_NAME_TYPE_STR 1
+static const AttrNameMap attr_names[]=
+{
+ { "SizeLength", ATTR_NAME_TYPE_INT,
+ offsetof(PayloadContext, sizelength) },
+ { "IndexLength", ATTR_NAME_TYPE_INT,
+ offsetof(PayloadContext, indexlength) },
+ { "IndexDeltaLength", ATTR_NAME_TYPE_INT,
+ offsetof(PayloadContext, indexdeltalength) },
+ { "profile-level-id", ATTR_NAME_TYPE_INT,
+ offsetof(PayloadContext, profile_level_id) },
+ { "StreamType", ATTR_NAME_TYPE_INT,
+ offsetof(PayloadContext, streamtype) },
+ { "mode", ATTR_NAME_TYPE_STR,
+ offsetof(PayloadContext, mode) },
+ { NULL, -1, -1 },
+};
+
+static PayloadContext *new_context(void)
+{
+ return av_mallocz(sizeof(PayloadContext));
+}
+
+static void free_context(PayloadContext * data)
+{
+ int i;
+ for (i = 0; i < data->nb_au_headers; i++) {
+ /* according to rtp_parse_mp4_au, we treat multiple
+ * au headers as one, so nb_au_headers is always 1.
+ * loop anyway in case this changes.
+ * (note: changes done carelessly might lead to a double free)
+ */
+ av_free(&data->au_headers[i]);
}
- return len;
+ av_free(data->mode);
+ av_free(data);
}
static int parse_fmtp_config(AVCodecContext * codec, char *value)
{
/* decode the hexa encoded parameter */
- int len = hex_to_data(NULL, value);
- if (codec->extradata)
- av_free(codec->extradata);
+ int len = ff_hex_to_data(NULL, value);
+ av_free(codec->extradata);
codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE);
if (!codec->extradata)
return AVERROR(ENOMEM);
codec->extradata_size = len;
- hex_to_data(codec->extradata, value);
+ ff_hex_to_data(codec->extradata, value);
return 0;
}
-static int parse_sdp_line(AVFormatContext *s, int st_index,
- PayloadContext *data, const char *line)
+static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf)
{
- const char *p;
- char value[4096], attr[25];
- int res = 0;
- AVCodecContext* codec = s->streams[st_index]->codec;
-
- if (av_strstart(line, "fmtp:", &p)) {
- // remove protocol identifier
- while (*p && *p == ' ') p++; // strip spaces
- while (*p && *p != ' ') p++; // eat protocol identifier
- while (*p && *p == ' ') p++; // strip trailing spaces
-
- while (ff_rtsp_next_attr_and_value(&p,
- attr, sizeof(attr),
- value, sizeof(value))) {
- if (!strcmp(attr, "config")) {
- res = parse_fmtp_config(codec, value);
-
- if (res < 0)
- return res;
+ int au_headers_length, au_header_size, i;
+ GetBitContext getbitcontext;
+
+ /* decode the first 2 bytes where the AUHeader sections are stored
+ length in bits */
+ au_headers_length = AV_RB16(buf);
+
+ if (au_headers_length > RTP_MAX_PACKET_LENGTH)
+ return -1;
+
+ data->au_headers_length_bytes = (au_headers_length + 7) / 8;
+
+ /* skip AU headers length section (2 bytes) */
+ buf += 2;
+
+ init_get_bits(&getbitcontext, buf, data->au_headers_length_bytes * 8);
+
+ /* XXX: Wrong if optionnal additional sections are present (cts, dts etc...) */
+ au_header_size = data->sizelength + data->indexlength;
+ if (au_header_size <= 0 || (au_headers_length % au_header_size != 0))
+ return -1;
+
+ data->nb_au_headers = au_headers_length / au_header_size;
+ if (!data->au_headers || data->au_headers_allocated < data->nb_au_headers) {
+ av_free(data->au_headers);
+ data->au_headers = av_malloc(sizeof(struct AUHeaders) * data->nb_au_headers);
+ 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->nb_au_headers = 1;
+
+ return 0;
+}
+
+
+/* Follows RFC 3640 */
+static int aac_parse_packet(AVFormatContext *ctx,
+ PayloadContext *data,
+ AVStream *st,
+ AVPacket *pkt,
+ uint32_t *timestamp,
+ const uint8_t *buf, int len, int flags)
+{
+ if (rtp_parse_mp4_au(data, buf))
+ return -1;
+
+ buf += data->au_headers_length_bytes + 2;
+ len -= data->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, data->au_headers[0].size);
+ memcpy(pkt->data, buf, data->au_headers[0].size);
+
+ pkt->stream_index = st->index;
+ return 0;
+}
+
+static int parse_fmtp(AVStream *stream, PayloadContext *data,
+ char *attr, char *value)
+{
+ AVCodecContext *codec = stream->codec;
+ int res, i;
+
+ if (!strcmp(attr, "config")) {
+ res = parse_fmtp_config(codec, value);
+
+ if (res < 0)
+ return res;
+ }
+
+ if (codec->codec_id == CODEC_ID_AAC) {
+ /* 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 *)data+
+ attr_names[i].offset) = atoi(value);
+ } else if (attr_names[i].type == ATTR_NAME_TYPE_STR)
+ *(char **)((char *)data+
+ attr_names[i].offset) = av_strdup(value);
}
}
}
-
return 0;
+}
+static int parse_sdp_line(AVFormatContext *s, int st_index,
+ PayloadContext *data, const char *line)
+{
+ const char *p;
+
+ if (av_strstart(line, "fmtp:", &p))
+ return ff_parse_fmtp(s->streams[st_index], data, p, parse_fmtp);
+
+ return 0;
}
RTPDynamicProtocolHandler ff_mp4v_es_dynamic_handler = {
.codec_type = AVMEDIA_TYPE_AUDIO,
.codec_id = CODEC_ID_AAC,
.parse_sdp_a_line = parse_sdp_line,
- .open = NULL,
- .close = NULL,
- .parse_packet = NULL
+ .open = new_context,
+ .close = free_context,
+ .parse_packet = aac_parse_packet
};