Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6148 | serge | 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 FFmpeg. |
||
7 | * |
||
8 | * FFmpeg 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 | * FFmpeg 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 FFmpeg; 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/attributes.h" |
||
33 | #include "libavutil/avstring.h" |
||
34 | #include "libavcodec/get_bits.h" |
||
35 | |||
36 | /** Structure listing useful vars to parse RTP packet payload */ |
||
37 | struct PayloadContext { |
||
38 | int sizelength; |
||
39 | int indexlength; |
||
40 | int indexdeltalength; |
||
41 | int profile_level_id; |
||
42 | int streamtype; |
||
43 | int objecttype; |
||
44 | char *mode; |
||
45 | |||
46 | /** mpeg 4 AU headers */ |
||
47 | struct AUHeaders { |
||
48 | int size; |
||
49 | int index; |
||
50 | int cts_flag; |
||
51 | int cts; |
||
52 | int dts_flag; |
||
53 | int dts; |
||
54 | int rap_flag; |
||
55 | int streamstate; |
||
56 | } *au_headers; |
||
57 | int au_headers_allocated; |
||
58 | int nb_au_headers; |
||
59 | int au_headers_length_bytes; |
||
60 | int cur_au_index; |
||
61 | |||
62 | uint8_t buf[RTP_MAX_PACKET_LENGTH]; |
||
63 | int buf_pos, buf_size; |
||
64 | }; |
||
65 | |||
66 | typedef struct { |
||
67 | const char *str; |
||
68 | uint16_t type; |
||
69 | uint32_t offset; |
||
70 | } AttrNameMap; |
||
71 | |||
72 | /* All known fmtp parameters and the corresponding RTPAttrTypeEnum */ |
||
73 | #define ATTR_NAME_TYPE_INT 0 |
||
74 | #define ATTR_NAME_TYPE_STR 1 |
||
75 | static const AttrNameMap attr_names[] = { |
||
76 | { "SizeLength", ATTR_NAME_TYPE_INT, |
||
77 | offsetof(PayloadContext, sizelength) }, |
||
78 | { "IndexLength", ATTR_NAME_TYPE_INT, |
||
79 | offsetof(PayloadContext, indexlength) }, |
||
80 | { "IndexDeltaLength", ATTR_NAME_TYPE_INT, |
||
81 | offsetof(PayloadContext, indexdeltalength) }, |
||
82 | { "profile-level-id", ATTR_NAME_TYPE_INT, |
||
83 | offsetof(PayloadContext, profile_level_id) }, |
||
84 | { "StreamType", ATTR_NAME_TYPE_INT, |
||
85 | offsetof(PayloadContext, streamtype) }, |
||
86 | { "mode", ATTR_NAME_TYPE_STR, |
||
87 | offsetof(PayloadContext, mode) }, |
||
88 | { NULL, -1, -1 }, |
||
89 | }; |
||
90 | |||
91 | static PayloadContext *new_context(void) |
||
92 | { |
||
93 | return av_mallocz(sizeof(PayloadContext)); |
||
94 | } |
||
95 | |||
96 | static void free_context(PayloadContext *data) |
||
97 | { |
||
98 | av_free(data->au_headers); |
||
99 | av_free(data->mode); |
||
100 | av_free(data); |
||
101 | } |
||
102 | |||
103 | static int parse_fmtp_config(AVCodecContext *codec, char *value) |
||
104 | { |
||
105 | /* decode the hexa encoded parameter */ |
||
106 | int len = ff_hex_to_data(NULL, value); |
||
107 | av_free(codec->extradata); |
||
108 | if (ff_alloc_extradata(codec, len)) |
||
109 | return AVERROR(ENOMEM); |
||
110 | ff_hex_to_data(codec->extradata, value); |
||
111 | return 0; |
||
112 | } |
||
113 | |||
114 | static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf, int len) |
||
115 | { |
||
116 | int au_headers_length, au_header_size, i; |
||
117 | GetBitContext getbitcontext; |
||
118 | |||
119 | if (len < 2) |
||
120 | return AVERROR_INVALIDDATA; |
||
121 | |||
122 | /* decode the first 2 bytes where the AUHeader sections are stored |
||
123 | length in bits */ |
||
124 | au_headers_length = AV_RB16(buf); |
||
125 | |||
126 | if (au_headers_length > RTP_MAX_PACKET_LENGTH) |
||
127 | return -1; |
||
128 | |||
129 | data->au_headers_length_bytes = (au_headers_length + 7) / 8; |
||
130 | |||
131 | /* skip AU headers length section (2 bytes) */ |
||
132 | buf += 2; |
||
133 | len -= 2; |
||
134 | |||
135 | if (len < data->au_headers_length_bytes) |
||
136 | return AVERROR_INVALIDDATA; |
||
137 | |||
138 | init_get_bits(&getbitcontext, buf, data->au_headers_length_bytes * 8); |
||
139 | |||
140 | /* XXX: Wrong if optional additional sections are present (cts, dts etc...) */ |
||
141 | au_header_size = data->sizelength + data->indexlength; |
||
142 | if (au_header_size <= 0 || (au_headers_length % au_header_size != 0)) |
||
143 | return -1; |
||
144 | |||
145 | data->nb_au_headers = au_headers_length / au_header_size; |
||
146 | if (!data->au_headers || data->au_headers_allocated < data->nb_au_headers) { |
||
147 | av_free(data->au_headers); |
||
148 | data->au_headers = av_malloc(sizeof(struct AUHeaders) * data->nb_au_headers); |
||
149 | if (!data->au_headers) |
||
150 | return AVERROR(ENOMEM); |
||
151 | data->au_headers_allocated = data->nb_au_headers; |
||
152 | } |
||
153 | |||
154 | for (i = 0; i < data->nb_au_headers; ++i) { |
||
155 | data->au_headers[i].size = get_bits_long(&getbitcontext, data->sizelength); |
||
156 | data->au_headers[i].index = get_bits_long(&getbitcontext, data->indexlength); |
||
157 | } |
||
158 | |||
159 | return 0; |
||
160 | } |
||
161 | |||
162 | |||
163 | /* Follows RFC 3640 */ |
||
164 | static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data, |
||
165 | AVStream *st, AVPacket *pkt, uint32_t *timestamp, |
||
166 | const uint8_t *buf, int len, uint16_t seq, |
||
167 | int flags) |
||
168 | { |
||
169 | int ret; |
||
170 | |||
171 | if (!buf) { |
||
172 | if (data->cur_au_index > data->nb_au_headers) |
||
173 | return AVERROR_INVALIDDATA; |
||
174 | if (data->buf_size - data->buf_pos < data->au_headers[data->cur_au_index].size) |
||
175 | return AVERROR_INVALIDDATA; |
||
176 | if ((ret = av_new_packet(pkt, data->au_headers[data->cur_au_index].size)) < 0) |
||
177 | return ret; |
||
178 | memcpy(pkt->data, &data->buf[data->buf_pos], data->au_headers[data->cur_au_index].size); |
||
179 | data->buf_pos += data->au_headers[data->cur_au_index].size; |
||
180 | pkt->stream_index = st->index; |
||
181 | data->cur_au_index++; |
||
182 | return data->cur_au_index < data->nb_au_headers; |
||
183 | } |
||
184 | |||
185 | if (rtp_parse_mp4_au(data, buf, len)) |
||
186 | return -1; |
||
187 | |||
188 | buf += data->au_headers_length_bytes + 2; |
||
189 | len -= data->au_headers_length_bytes + 2; |
||
190 | |||
191 | if (len < data->au_headers[0].size) |
||
192 | return AVERROR_INVALIDDATA; |
||
193 | if ((ret = av_new_packet(pkt, data->au_headers[0].size)) < 0) |
||
194 | return ret; |
||
195 | memcpy(pkt->data, buf, data->au_headers[0].size); |
||
196 | len -= data->au_headers[0].size; |
||
197 | buf += data->au_headers[0].size; |
||
198 | pkt->stream_index = st->index; |
||
199 | |||
200 | if (len > 0 && data->nb_au_headers > 1) { |
||
201 | data->buf_size = FFMIN(len, sizeof(data->buf)); |
||
202 | memcpy(data->buf, buf, data->buf_size); |
||
203 | data->cur_au_index = 1; |
||
204 | data->buf_pos = 0; |
||
205 | return 1; |
||
206 | } |
||
207 | |||
208 | return 0; |
||
209 | } |
||
210 | |||
211 | static int parse_fmtp(AVStream *stream, PayloadContext *data, |
||
212 | char *attr, char *value) |
||
213 | { |
||
214 | AVCodecContext *codec = stream->codec; |
||
215 | int res, i; |
||
216 | |||
217 | if (!strcmp(attr, "config")) { |
||
218 | res = parse_fmtp_config(codec, value); |
||
219 | |||
220 | if (res < 0) |
||
221 | return res; |
||
222 | } |
||
223 | |||
224 | if (codec->codec_id == AV_CODEC_ID_AAC) { |
||
225 | /* Looking for a known attribute */ |
||
226 | for (i = 0; attr_names[i].str; ++i) { |
||
227 | if (!av_strcasecmp(attr, attr_names[i].str)) { |
||
228 | if (attr_names[i].type == ATTR_NAME_TYPE_INT) { |
||
229 | *(int *)((char *)data+ |
||
230 | attr_names[i].offset) = atoi(value); |
||
231 | } else if (attr_names[i].type == ATTR_NAME_TYPE_STR) |
||
232 | *(char **)((char *)data+ |
||
233 | attr_names[i].offset) = av_strdup(value); |
||
234 | } |
||
235 | } |
||
236 | } |
||
237 | return 0; |
||
238 | } |
||
239 | |||
240 | static int parse_sdp_line(AVFormatContext *s, int st_index, |
||
241 | PayloadContext *data, const char *line) |
||
242 | { |
||
243 | const char *p; |
||
244 | |||
245 | if (st_index < 0) |
||
246 | return 0; |
||
247 | |||
248 | if (av_strstart(line, "fmtp:", &p)) |
||
249 | return ff_parse_fmtp(s->streams[st_index], data, p, parse_fmtp); |
||
250 | |||
251 | return 0; |
||
252 | } |
||
253 | |||
254 | static av_cold int init_video(AVFormatContext *s, int st_index, |
||
255 | PayloadContext *data) |
||
256 | { |
||
257 | if (st_index < 0) |
||
258 | return 0; |
||
259 | s->streams[st_index]->need_parsing = AVSTREAM_PARSE_FULL; |
||
260 | return 0; |
||
261 | } |
||
262 | |||
263 | RTPDynamicProtocolHandler ff_mp4v_es_dynamic_handler = { |
||
264 | .enc_name = "MP4V-ES", |
||
265 | .codec_type = AVMEDIA_TYPE_VIDEO, |
||
266 | .codec_id = AV_CODEC_ID_MPEG4, |
||
267 | .init = init_video, |
||
268 | .parse_sdp_a_line = parse_sdp_line, |
||
269 | }; |
||
270 | |||
271 | RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler = { |
||
272 | .enc_name = "mpeg4-generic", |
||
273 | .codec_type = AVMEDIA_TYPE_AUDIO, |
||
274 | .codec_id = AV_CODEC_ID_AAC, |
||
275 | .parse_sdp_a_line = parse_sdp_line, |
||
276 | .alloc = new_context, |
||
277 | .free = free_context, |
||
278 | .parse_packet = aac_parse_packet |
||
279 | };>>>>>>>>>>=>>> |