]> git.sesse.net Git - ffmpeg/blob - libavformat/rtpdec_mpeg4.c
mpegvideo: allocate scratch buffers after linesize is known
[ffmpeg] / libavformat / rtpdec_mpeg4.c
1 /*
2  * Common code for the RTP depacketization of MPEG-4 formats.
3  * Copyright (c) 2010 Fabrice Bellard
4  *                    Romain Degez
5  *
6  * This file is part of Libav.
7  *
8  * Libav is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * Libav is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with Libav; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * @file
25  * @brief MPEG4 / RTP Code
26  * @author Fabrice Bellard
27  * @author Romain Degez
28  */
29
30 #include "rtpdec_formats.h"
31 #include "internal.h"
32 #include "libavutil/avstring.h"
33 #include "libavcodec/get_bits.h"
34
35 /** Structure listing useful vars to parse RTP packet payload */
36 struct PayloadContext {
37     int sizelength;
38     int indexlength;
39     int indexdeltalength;
40     int profile_level_id;
41     int streamtype;
42     int objecttype;
43     char *mode;
44
45     /** mpeg 4 AU headers */
46     struct AUHeaders {
47         int size;
48         int index;
49         int cts_flag;
50         int cts;
51         int dts_flag;
52         int dts;
53         int rap_flag;
54         int streamstate;
55     } *au_headers;
56     int au_headers_allocated;
57     int nb_au_headers;
58     int au_headers_length_bytes;
59     int cur_au_index;
60 };
61
62 typedef struct {
63     const char *str;
64     uint16_t    type;
65     uint32_t    offset;
66 } AttrNameMap;
67
68 /* All known fmtp parameters and the corresponding RTPAttrTypeEnum */
69 #define ATTR_NAME_TYPE_INT 0
70 #define ATTR_NAME_TYPE_STR 1
71 static const AttrNameMap attr_names[] = {
72     { "SizeLength",       ATTR_NAME_TYPE_INT,
73       offsetof(PayloadContext, sizelength) },
74     { "IndexLength",      ATTR_NAME_TYPE_INT,
75       offsetof(PayloadContext, indexlength) },
76     { "IndexDeltaLength", ATTR_NAME_TYPE_INT,
77       offsetof(PayloadContext, indexdeltalength) },
78     { "profile-level-id", ATTR_NAME_TYPE_INT,
79       offsetof(PayloadContext, profile_level_id) },
80     { "StreamType",       ATTR_NAME_TYPE_INT,
81       offsetof(PayloadContext, streamtype) },
82     { "mode",             ATTR_NAME_TYPE_STR,
83       offsetof(PayloadContext, mode) },
84     { NULL, -1, -1 },
85 };
86
87 static PayloadContext *new_context(void)
88 {
89     return av_mallocz(sizeof(PayloadContext));
90 }
91
92 static void free_context(PayloadContext *data)
93 {
94     av_free(data->au_headers);
95     av_free(data->mode);
96     av_free(data);
97 }
98
99 static int parse_fmtp_config(AVCodecContext *codec, char *value)
100 {
101     /* decode the hexa encoded parameter */
102     int len = ff_hex_to_data(NULL, value);
103     av_free(codec->extradata);
104     codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE);
105     if (!codec->extradata)
106         return AVERROR(ENOMEM);
107     codec->extradata_size = len;
108     ff_hex_to_data(codec->extradata, value);
109     return 0;
110 }
111
112 static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf)
113 {
114     int au_headers_length, au_header_size, i;
115     GetBitContext getbitcontext;
116
117     /* decode the first 2 bytes where the AUHeader sections are stored
118        length in bits */
119     au_headers_length = AV_RB16(buf);
120
121     if (au_headers_length > RTP_MAX_PACKET_LENGTH)
122       return -1;
123
124     data->au_headers_length_bytes = (au_headers_length + 7) / 8;
125
126     /* skip AU headers length section (2 bytes) */
127     buf += 2;
128
129     init_get_bits(&getbitcontext, buf, data->au_headers_length_bytes * 8);
130
131     /* XXX: Wrong if optionnal additional sections are present (cts, dts etc...) */
132     au_header_size = data->sizelength + data->indexlength;
133     if (au_header_size <= 0 || (au_headers_length % au_header_size != 0))
134         return -1;
135
136     data->nb_au_headers = au_headers_length / au_header_size;
137     if (!data->au_headers || data->au_headers_allocated < data->nb_au_headers) {
138         av_free(data->au_headers);
139         data->au_headers = av_malloc(sizeof(struct AUHeaders) * data->nb_au_headers);
140         data->au_headers_allocated = data->nb_au_headers;
141     }
142
143     /* XXX: We handle multiple AU Section as only one (need to fix this for interleaving)
144        In my test, the FAAD decoder does not behave correctly when sending each AU one by one
145        but does when sending the whole as one big packet...  */
146     data->au_headers[0].size = 0;
147     data->au_headers[0].index = 0;
148     for (i = 0; i < data->nb_au_headers; ++i) {
149         data->au_headers[0].size += get_bits_long(&getbitcontext, data->sizelength);
150         data->au_headers[0].index = get_bits_long(&getbitcontext, data->indexlength);
151     }
152
153     data->nb_au_headers = 1;
154
155     return 0;
156 }
157
158
159 /* Follows RFC 3640 */
160 static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
161                             AVStream *st, AVPacket *pkt, uint32_t *timestamp,
162                             const uint8_t *buf, int len, int flags)
163 {
164     if (rtp_parse_mp4_au(data, buf))
165         return -1;
166
167     buf += data->au_headers_length_bytes + 2;
168     len -= data->au_headers_length_bytes + 2;
169
170     /* XXX: Fixme we only handle the case where rtp_parse_mp4_au define
171                     one au_header */
172     av_new_packet(pkt, data->au_headers[0].size);
173     memcpy(pkt->data, buf, data->au_headers[0].size);
174
175     pkt->stream_index = st->index;
176     return 0;
177 }
178
179 static int parse_fmtp(AVStream *stream, PayloadContext *data,
180                       char *attr, char *value)
181 {
182     AVCodecContext *codec = stream->codec;
183     int res, i;
184
185     if (!strcmp(attr, "config")) {
186         res = parse_fmtp_config(codec, value);
187
188         if (res < 0)
189             return res;
190     }
191
192     if (codec->codec_id == AV_CODEC_ID_AAC) {
193         /* Looking for a known attribute */
194         for (i = 0; attr_names[i].str; ++i) {
195             if (!av_strcasecmp(attr, attr_names[i].str)) {
196                 if (attr_names[i].type == ATTR_NAME_TYPE_INT) {
197                     *(int *)((char *)data+
198                         attr_names[i].offset) = atoi(value);
199                 } else if (attr_names[i].type == ATTR_NAME_TYPE_STR)
200                     *(char **)((char *)data+
201                         attr_names[i].offset) = av_strdup(value);
202             }
203         }
204     }
205     return 0;
206 }
207
208 static int parse_sdp_line(AVFormatContext *s, int st_index,
209                           PayloadContext *data, const char *line)
210 {
211     const char *p;
212
213     if (st_index < 0)
214         return 0;
215
216     if (av_strstart(line, "fmtp:", &p))
217         return ff_parse_fmtp(s->streams[st_index], data, p, parse_fmtp);
218
219     return 0;
220 }
221
222 RTPDynamicProtocolHandler ff_mp4v_es_dynamic_handler = {
223     .enc_name           = "MP4V-ES",
224     .codec_type         = AVMEDIA_TYPE_VIDEO,
225     .codec_id           = AV_CODEC_ID_MPEG4,
226     .parse_sdp_a_line   = parse_sdp_line,
227 };
228
229 RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler = {
230     .enc_name           = "mpeg4-generic",
231     .codec_type         = AVMEDIA_TYPE_AUDIO,
232     .codec_id           = AV_CODEC_ID_AAC,
233     .parse_sdp_a_line   = parse_sdp_line,
234     .alloc              = new_context,
235     .free               = free_context,
236     .parse_packet       = aac_parse_packet
237 };