ffmpeg / libavformat / rtpdec_mpeg4.c @ 9dd94f83
History | View | Annotate | Download (7.66 KB)
1 | 9b3788ef | Josh Allmann | /**
|
---|---|---|---|
2 | * Common code for the RTP depacketization of MPEG-4 formats.
|
||
3 | * Copyright (c) 2010 Fabrice Bellard
|
||
4 | * Romain Degez
|
||
5 | *
|
||
6 | 2912e87a | Mans Rullgard | * This file is part of Libav.
|
7 | 9b3788ef | Josh Allmann | *
|
8 | 2912e87a | Mans Rullgard | * Libav is free software; you can redistribute it and/or
|
9 | 9b3788ef | Josh Allmann | * 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 | 2912e87a | Mans Rullgard | * Libav is distributed in the hope that it will be useful,
|
14 | 9b3788ef | Josh Allmann | * 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 | 2912e87a | Mans Rullgard | * License along with Libav; if not, write to the Free Software
|
20 | 9b3788ef | Josh Allmann | * 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 | 965a3ddb | Martin Storsjö | #include "rtpdec_formats.h" |
31 | 1537817e | Martin Storsjö | #include "internal.h" |
32 | 9b3788ef | Josh Allmann | #include "libavutil/avstring.h" |
33 | 73e6c53e | Josh Allmann | #include "libavcodec/get_bits.h" |
34 | 7fc8ac7f | Josh Allmann | #include <strings.h> |
35 | |||
36 | ca937a55 | Josh Allmann | /** Structure listing useful vars to parse RTP packet payload*/
|
37 | struct PayloadContext
|
||
38 | { |
||
39 | int sizelength;
|
||
40 | int indexlength;
|
||
41 | int indexdeltalength;
|
||
42 | int profile_level_id;
|
||
43 | int streamtype;
|
||
44 | int objecttype;
|
||
45 | char *mode;
|
||
46 | |||
47 | /** mpeg 4 AU headers */
|
||
48 | struct AUHeaders {
|
||
49 | int size;
|
||
50 | int index;
|
||
51 | int cts_flag;
|
||
52 | int cts;
|
||
53 | int dts_flag;
|
||
54 | int dts;
|
||
55 | int rap_flag;
|
||
56 | int streamstate;
|
||
57 | } *au_headers; |
||
58 | int au_headers_allocated;
|
||
59 | int nb_au_headers;
|
||
60 | int au_headers_length_bytes;
|
||
61 | int cur_au_index;
|
||
62 | }; |
||
63 | 9b3788ef | Josh Allmann | |
64 | 7fc8ac7f | Josh Allmann | typedef struct { |
65 | const char *str; |
||
66 | uint16_t type; |
||
67 | uint32_t offset; |
||
68 | } AttrNameMap; |
||
69 | |||
70 | /* All known fmtp parameters and the corresponding RTPAttrTypeEnum */
|
||
71 | #define ATTR_NAME_TYPE_INT 0 |
||
72 | #define ATTR_NAME_TYPE_STR 1 |
||
73 | static const AttrNameMap attr_names[]= |
||
74 | { |
||
75 | { "SizeLength", ATTR_NAME_TYPE_INT,
|
||
76 | ca937a55 | Josh Allmann | offsetof(PayloadContext, sizelength) }, |
77 | 7fc8ac7f | Josh Allmann | { "IndexLength", ATTR_NAME_TYPE_INT,
|
78 | ca937a55 | Josh Allmann | offsetof(PayloadContext, indexlength) }, |
79 | 7fc8ac7f | Josh Allmann | { "IndexDeltaLength", ATTR_NAME_TYPE_INT,
|
80 | ca937a55 | Josh Allmann | offsetof(PayloadContext, indexdeltalength) }, |
81 | 7fc8ac7f | Josh Allmann | { "profile-level-id", ATTR_NAME_TYPE_INT,
|
82 | ca937a55 | Josh Allmann | offsetof(PayloadContext, profile_level_id) }, |
83 | 7fc8ac7f | Josh Allmann | { "StreamType", ATTR_NAME_TYPE_INT,
|
84 | ca937a55 | Josh Allmann | offsetof(PayloadContext, streamtype) }, |
85 | 7fc8ac7f | Josh Allmann | { "mode", ATTR_NAME_TYPE_STR,
|
86 | ca937a55 | Josh Allmann | offsetof(PayloadContext, mode) }, |
87 | 7fc8ac7f | Josh Allmann | { NULL, -1, -1 }, |
88 | }; |
||
89 | |||
90 | ca937a55 | Josh Allmann | static PayloadContext *new_context(void) |
91 | { |
||
92 | return av_mallocz(sizeof(PayloadContext)); |
||
93 | } |
||
94 | |||
95 | static void free_context(PayloadContext * data) |
||
96 | { |
||
97 | int i;
|
||
98 | for (i = 0; i < data->nb_au_headers; i++) { |
||
99 | /* according to rtp_parse_mp4_au, we treat multiple
|
||
100 | * au headers as one, so nb_au_headers is always 1.
|
||
101 | * loop anyway in case this changes.
|
||
102 | * (note: changes done carelessly might lead to a double free)
|
||
103 | */
|
||
104 | av_free(&data->au_headers[i]); |
||
105 | } |
||
106 | av_free(data->mode); |
||
107 | av_free(data); |
||
108 | } |
||
109 | |||
110 | 9b3788ef | Josh Allmann | static int parse_fmtp_config(AVCodecContext * codec, char *value) |
111 | { |
||
112 | /* decode the hexa encoded parameter */
|
||
113 | 311baee7 | Martin Storsjö | int len = ff_hex_to_data(NULL, value); |
114 | 437fb1c8 | Clément Bœsch | av_free(codec->extradata); |
115 | 9b3788ef | Josh Allmann | codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); |
116 | if (!codec->extradata)
|
||
117 | return AVERROR(ENOMEM);
|
||
118 | codec->extradata_size = len; |
||
119 | 311baee7 | Martin Storsjö | ff_hex_to_data(codec->extradata, value); |
120 | 9b3788ef | Josh Allmann | return 0; |
121 | } |
||
122 | |||
123 | c47f567c | Josh Allmann | static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf) |
124 | 73e6c53e | Josh Allmann | { |
125 | int au_headers_length, au_header_size, i;
|
||
126 | GetBitContext getbitcontext; |
||
127 | |||
128 | /* decode the first 2 bytes where the AUHeader sections are stored
|
||
129 | length in bits */
|
||
130 | au_headers_length = AV_RB16(buf); |
||
131 | |||
132 | if (au_headers_length > RTP_MAX_PACKET_LENGTH)
|
||
133 | return -1; |
||
134 | |||
135 | c47f567c | Josh Allmann | data->au_headers_length_bytes = (au_headers_length + 7) / 8; |
136 | 73e6c53e | Josh Allmann | |
137 | /* skip AU headers length section (2 bytes) */
|
||
138 | buf += 2;
|
||
139 | |||
140 | c47f567c | Josh Allmann | init_get_bits(&getbitcontext, buf, data->au_headers_length_bytes * 8);
|
141 | 73e6c53e | Josh Allmann | |
142 | /* XXX: Wrong if optionnal additional sections are present (cts, dts etc...) */
|
||
143 | c47f567c | Josh Allmann | au_header_size = data->sizelength + data->indexlength; |
144 | 73e6c53e | Josh Allmann | if (au_header_size <= 0 || (au_headers_length % au_header_size != 0)) |
145 | return -1; |
||
146 | |||
147 | c47f567c | Josh Allmann | data->nb_au_headers = au_headers_length / au_header_size; |
148 | if (!data->au_headers || data->au_headers_allocated < data->nb_au_headers) {
|
||
149 | av_free(data->au_headers); |
||
150 | data->au_headers = av_malloc(sizeof(struct AUHeaders) * data->nb_au_headers); |
||
151 | data->au_headers_allocated = data->nb_au_headers; |
||
152 | 73e6c53e | Josh Allmann | } |
153 | |||
154 | /* XXX: We handle multiple AU Section as only one (need to fix this for interleaving)
|
||
155 | In my test, the FAAD decoder does not behave correctly when sending each AU one by one
|
||
156 | but does when sending the whole as one big packet... */
|
||
157 | c47f567c | Josh Allmann | data->au_headers[0].size = 0; |
158 | data->au_headers[0].index = 0; |
||
159 | for (i = 0; i < data->nb_au_headers; ++i) { |
||
160 | data->au_headers[0].size += get_bits_long(&getbitcontext, data->sizelength);
|
||
161 | data->au_headers[0].index = get_bits_long(&getbitcontext, data->indexlength);
|
||
162 | 73e6c53e | Josh Allmann | } |
163 | |||
164 | c47f567c | Josh Allmann | data->nb_au_headers = 1;
|
165 | 73e6c53e | Josh Allmann | |
166 | return 0; |
||
167 | } |
||
168 | |||
169 | |||
170 | /* Follows RFC 3640 */
|
||
171 | static int aac_parse_packet(AVFormatContext *ctx, |
||
172 | c47f567c | Josh Allmann | PayloadContext *data, |
173 | 73e6c53e | Josh Allmann | AVStream *st, |
174 | AVPacket *pkt, |
||
175 | uint32_t *timestamp, |
||
176 | const uint8_t *buf, int len, int flags) |
||
177 | { |
||
178 | c47f567c | Josh Allmann | if (rtp_parse_mp4_au(data, buf))
|
179 | 73e6c53e | Josh Allmann | return -1; |
180 | |||
181 | c47f567c | Josh Allmann | buf += data->au_headers_length_bytes + 2;
|
182 | len -= data->au_headers_length_bytes + 2;
|
||
183 | 73e6c53e | Josh Allmann | |
184 | /* XXX: Fixme we only handle the case where rtp_parse_mp4_au define
|
||
185 | one au_header */
|
||
186 | c47f567c | Josh Allmann | av_new_packet(pkt, data->au_headers[0].size);
|
187 | memcpy(pkt->data, buf, data->au_headers[0].size);
|
||
188 | 73e6c53e | Josh Allmann | |
189 | pkt->stream_index = st->index; |
||
190 | return 0; |
||
191 | } |
||
192 | |||
193 | efc6d451 | Josh Allmann | static int parse_fmtp(AVStream *stream, PayloadContext *data, |
194 | char *attr, char *value) |
||
195 | 9b3788ef | Josh Allmann | { |
196 | efc6d451 | Josh Allmann | AVCodecContext *codec = stream->codec; |
197 | int res, i;
|
||
198 | |||
199 | 4332bfbf | Josh Allmann | if (!strcmp(attr, "config")) { |
200 | res = parse_fmtp_config(codec, value); |
||
201 | 9b3788ef | Josh Allmann | |
202 | 4332bfbf | Josh Allmann | if (res < 0) |
203 | return res;
|
||
204 | } |
||
205 | 7fc8ac7f | Josh Allmann | |
206 | 4332bfbf | Josh Allmann | if (codec->codec_id == CODEC_ID_AAC) {
|
207 | /* Looking for a known attribute */
|
||
208 | for (i = 0; attr_names[i].str; ++i) { |
||
209 | if (!strcasecmp(attr, attr_names[i].str)) {
|
||
210 | if (attr_names[i].type == ATTR_NAME_TYPE_INT) {
|
||
211 | *(int *)((char *)data+ |
||
212 | attr_names[i].offset) = atoi(value); |
||
213 | } else if (attr_names[i].type == ATTR_NAME_TYPE_STR) |
||
214 | *(char **)((char *)data+ |
||
215 | attr_names[i].offset) = av_strdup(value); |
||
216 | 7fc8ac7f | Josh Allmann | } |
217 | 9b3788ef | Josh Allmann | } |
218 | 4332bfbf | Josh Allmann | } |
219 | return 0; |
||
220 | } |
||
221 | 9b3788ef | Josh Allmann | |
222 | efc6d451 | Josh Allmann | static int parse_sdp_line(AVFormatContext *s, int st_index, |
223 | PayloadContext *data, const char *line) |
||
224 | { |
||
225 | const char *p; |
||
226 | |||
227 | if (av_strstart(line, "fmtp:", &p)) |
||
228 | return ff_parse_fmtp(s->streams[st_index], data, p, parse_fmtp);
|
||
229 | 9b3788ef | Josh Allmann | |
230 | efc6d451 | Josh Allmann | return 0; |
231 | 9b3788ef | Josh Allmann | } |
232 | |||
233 | RTPDynamicProtocolHandler ff_mp4v_es_dynamic_handler = { |
||
234 | .enc_name = "MP4V-ES",
|
||
235 | .codec_type = AVMEDIA_TYPE_VIDEO, |
||
236 | .codec_id = CODEC_ID_MPEG4, |
||
237 | .parse_sdp_a_line = parse_sdp_line, |
||
238 | .open = NULL,
|
||
239 | .close = NULL,
|
||
240 | .parse_packet = NULL
|
||
241 | }; |
||
242 | |||
243 | RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler = { |
||
244 | .enc_name = "mpeg4-generic",
|
||
245 | .codec_type = AVMEDIA_TYPE_AUDIO, |
||
246 | .codec_id = CODEC_ID_AAC, |
||
247 | .parse_sdp_a_line = parse_sdp_line, |
||
248 | ca937a55 | Josh Allmann | .open = new_context, |
249 | .close = free_context, |
||
250 | 73e6c53e | Josh Allmann | .parse_packet = aac_parse_packet |
251 | 9b3788ef | Josh Allmann | }; |